* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "includes.h"
#include "onefs.h"
+#include "onefs_config.h"
+#include "oplock_onefs.h"
+#include "smbd/globals.h"
extern const struct generic_mapping file_generic_mapping;
-extern bool global_client_failed_oplock_break;
-struct deferred_open_record {
- bool delayed_for_oplocks;
- bool failed; /* added for onefs_oplocks */
- struct file_id id;
+struct onefs_fsp_data {
+ uint64_t oplock_callback_id;
};
static NTSTATUS onefs_create_file_unixpath(connection_struct *conn,
uint64_t allocation_size,
struct security_descriptor *sd,
struct ea_list *ea_list,
-
files_struct **result,
int *pinfo,
+ struct onefs_fsp_data *fsp_data,
SMB_STRUCT_STAT *psbuf);
/****************************************************************************
int local_flags = flags;
bool file_existed = VALID_STAT(*psbuf);
const char *wild;
+ char *base = NULL;
+ char *stream = NULL;
+ int base_fd = -1;
fsp->fh->fd = -1;
errno = EPERM;
flags |= O_NOFOLLOW;
}
#endif
- /* Don't request an oplock if oplocks are turned off for the
- * share. */
- if (!lp_oplocks(SNUM(conn)))
- oplock_request = 0;
+ /* Stream handling */
+ if (is_ntfs_stream_name(path)) {
+ status = onefs_split_ntfs_stream_name(talloc_tos(), path,
+ &base, &stream);
+ }
+ /* It's a stream, so pass in the base_fd */
+ if ((conn->fs_capabilities & FILE_NAMED_STREAMS) && stream != NULL) {
+ SMB_ASSERT(fsp->base_fsp);
+
+ /*
+ * We have never seen an oplock taken on a stream, and our
+ * current implementation doesn't support it. If a request is
+ * seen, log a loud error message and ignore the requested
+ * oplock.
+ */
+ if ((oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK) !=
+ NO_OPLOCK) {
+ DEBUG(0, ("Oplock(%d) being requested on a stream! "
+ "Ignoring oplock request: base=%s, "
+ "stream=%s\n",
+ oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK,
+ base, stream));
+ /* Recover by requesting NO_OPLOCK instead. */
+ oplock_request &= SAMBA_PRIVATE_OPLOCK_MASK;
+ }
+
+ DEBUG(10,("Opening a stream: base=%s(%d), stream=%s\n",
+ base, fsp->base_fsp->fh->fd, stream));
+
+ base_fd = fsp->base_fsp->fh->fd;
+ }
fsp->fh->fd = onefs_sys_create_file(conn,
- -1,
- path,
+ base_fd,
+ stream != NULL ? stream :
+ (base != NULL ? base : path),
access_mask,
open_access_mask,
share_access,
status = map_nt_error_from_unix(errno);
DEBUG(3,("Error opening file %s (%s) (local_flags=%d) "
- "(flags=%d)\n",
- path,nt_errstr(status),local_flags,flags));
+ "(flags=%d)\n",
+ path, strerror(errno), local_flags, flags));
return status;
}
exit_server("push_deferred_smb_message failed");
}
add_deferred_open(lck, req->mid, request_time, state->id);
-
- /*
- * Push the MID of this packet on the signing queue.
- * We only do this once, the first time we push the packet
- * onto the deferred open queue, as this has a side effect
- * of incrementing the response sequence number.
- */
-
- srv_defer_sign_response(req->mid);
}
static void schedule_defer_open(struct share_mode_lock *lck,
* measure here in case the other smbd is stuck
* somewhere else. */
- timeout = timeval_set(OPLOCK_BREAK_TIMEOUT*2, 0);
+ /*
+ * On OneFS, the kernel will always send an oplock_revoked message
+ * before this timeout is hit.
+ */
+ timeout = timeval_set(OPLOCK_BREAK_TIMEOUT*10, 0);
/* Nothing actually uses state.delayed_for_oplocks
but it's handy to differentiate in debug messages
a 1 second delay for share mode conflicts. */
state.delayed_for_oplocks = True;
- state.failed = False;
+ state.failed = false;
state.id = lck->id;
if (!request_timed_out(request_time, timeout)) {
struct security_descriptor *sd,
files_struct *fsp,
int *pinfo,
+ struct onefs_fsp_data *fsp_data,
SMB_STRUCT_STAT *psbuf)
{
int flags=0;
bool def_acl = False;
bool posix_open = False;
bool new_file_created = False;
+ bool clear_ads = False;
struct file_id id;
mode_t new_unx_mode = (mode_t)0;
mode_t unx_mode = (mode_t)0;
char *parent_dir;
const char *newname;
int granted_oplock;
- uint64 oplock_waiter;
+ uint64_t oplock_callback_id = 0;
uint32 createfile_attributes = 0;
ZERO_STRUCT(id);
create_disposition, create_options, unx_mode,
oplock_request));
+ /*
+ * Any non-stat-only open has the potential to contend oplocks, which
+ * means to avoid blocking in the kernel (which is unacceptable), the
+ * open must be deferred. In order to defer opens, req must not be
+ * NULL. The known cases of calling with a NULL req:
+ *
+ * 1. Open the base file of a stream: Always done stat-only
+ *
+ * 2. Open the stream: Oplocks are disallowed on streams, so an
+ * oplock will never be contended.
+ *
+ * 3. open_file_fchmod(), which is called from 3 places:
+ * A. try_chown: Posix acls only. Never called on onefs.
+ * B. set_ea_dos_attributes: Can't be called from onefs, because
+ * SMB_VFS_SETXATTR return ENOSYS.
+ * C. file_set_dos_mode: This would only happen if the "dos
+ * filemode" smb.conf parameter is set to yes. We ship with
+ * it off, but if a customer were to turn it on it would be
+ * bad.
+ */
+ if (req == NULL && !is_stat_open(access_mask) && !is_ntfs_stream_name(fname)) {
+ smb_panic("NULL req on a non-stat-open!");
+ }
+
if ((req == NULL) && ((oplock_request & INTERNAL_OPEN_ONLY) == 0)) {
DEBUG(0, ("No smb request but not an internal only open!\n"));
return NT_STATUS_INTERNAL_ERROR;
* (requiring delete access) then recreates it.
*/
case FILE_SUPERSEDE:
- /* If file exists replace/overwrite. If file doesn't
- * exist create. */
/**
* @todo: Clear all file attributes?
* http://www.osronline.com/article.cfm?article=302
* exist create.
*/
flags2 |= (O_CREAT | O_TRUNC);
+ clear_ads = true;
break;
case FILE_OVERWRITE_IF:
/* If file exists replace/overwrite. If file doesn't
* exist create. */
flags2 |= (O_CREAT | O_TRUNC);
+ clear_ads = true;
break;
case FILE_OPEN:
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
flags2 |= O_TRUNC;
+ clear_ads = true;
break;
case FILE_CREATE:
open_access_mask |= FILE_WRITE_DATA;
}
+ if (lp_parm_bool(SNUM(fsp->conn), PARM_ONEFS_TYPE,
+ PARM_IGNORE_SACLS, PARM_IGNORE_SACLS_DEFAULT)) {
+ access_mask &= ~SYSTEM_SECURITY_ACCESS;
+ }
+
DEBUG(10, ("onefs_open_file_ntcreate: fname=%s, after mapping "
"open_access_mask=%#x, access_mask=0x%x\n",
fname, open_access_mask, access_mask));
*/
flags2 &= ~(O_CREAT|O_TRUNC);
- /**
- * XXX: TODO
- * Apparently this is necessary because we ship with
- * lp_acl_check_permissions = no. It is set to no because our
- * ifs_createfile does the access check correctly. This check
- * was added in the last merge, and the question is why is it
- * necessary? Check out Bug 25547 and Bug 14596. The key is
- * to figure out what case this is covering, and do some
- * testing to see if it's actually necessary. If it is, maybe
- * it should go upstream in open.c.
- */
- if (!lp_acl_check_permissions(SNUM(conn)) &&
- (access_mask & DELETE_ACCESS)) {
+ /* Deny DELETE_ACCESS explicitly if the share is read only. */
+ if (access_mask & DELETE_ACCESS) {
return map_nt_error_from_unix(EACCES);
}
}
(unsigned int)unx_mode, (unsigned int)access_mask,
(unsigned int)open_access_mask));
- oplock_waiter = 1; //ifs_oplock_wait_record(mid);
-
- if (oplock_waiter == 0) {
- return NT_STATUS_NO_MEMORY;
+ /*
+ * Since the open is guaranteed to be stat only if req == NULL, a
+ * callback record is only needed if req != NULL.
+ */
+ if (req) {
+ SMB_ASSERT(fsp_data);
+ oplock_callback_id = onefs_oplock_wait_record(req->mid);
+ if (oplock_callback_id == 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ /*
+ * It is also already asserted it's either a stream or a
+ * stat-only open at this point.
+ */
+ SMB_ASSERT(fsp->oplock_type == NO_OPLOCK);
}
/* Do the open. */
access_mask,
open_access_mask,
fsp->oplock_type,
- oplock_waiter,
+ oplock_callback_id,
share_access,
create_options,
createfile_attributes,
goto cleanup_destroy;
}
/* Waiting for an oplock */
+ DEBUG(5,("Async createfile because a client has an "
+ "oplock on %s\n", fname));
+
SMB_ASSERT(req);
schedule_defer_open(lck, request_time, req);
goto cleanup;
* Normal error, for example EACCES
*/
cleanup_destroy:
- //destroy_ifs_callback_record(oplock_waiter);
+ if (oplock_callback_id != 0) {
+ destroy_onefs_callback_record(oplock_callback_id);
+ }
cleanup:
TALLOC_FREE(lck);
return status;
fsp->oplock_type = granted_oplock;
- /* XXX uncomment for oplocks */
- //ifs_set_oplock_callback(oplock_waiter, fsp);
- //fsp->oplock_callback_id = oplock_waiter;
+ if (oplock_callback_id != 0) {
+ onefs_set_oplock_callback(oplock_callback_id, fsp);
+ fsp_data->oplock_callback_id = oplock_callback_id;
+ } else {
+ SMB_ASSERT(fsp->oplock_type == NO_OPLOCK);
+ }
if (!file_existed) {
struct timespec old_write_time = get_mtimespec(psbuf);
SMB_ASSERT(lck != NULL);
+ /* Delete streams if create_disposition requires it */
+ if (file_existed && clear_ads && !is_ntfs_stream_name(fname)) {
+ status = delete_all_streams(conn, fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(lck);
+ fd_close(fsp);
+ return status;
+ }
+ }
+
/* note that we ignore failure for the following. It is
basically a hack for NFS, and NFS will never set one of
these only read them. Nobody but Samba can ever set a deny
}
}
+ if (fsp->oplock_type == LEVEL_II_OPLOCK &&
+ (!lp_level2_oplocks(SNUM(conn)) ||
+ !(global_client_caps & CAP_LEVEL_II_OPLOCKS))) {
+
+ DEBUG(5, ("Downgrading level2 oplock on open "
+ "because level2 oplocks = off\n"));
+
+ release_file_oplock(fsp);
+ }
+
if (info == FILE_WAS_OVERWRITTEN || info == FILE_WAS_CREATED ||
info == FILE_WAS_SUPERSEDED) {
new_file_created = True;
return NT_STATUS_OK;
}
-/*
- * If a main file is opened for delete, all streams need to be checked for
- * !FILE_SHARE_DELETE. Do this by opening with DELETE_ACCESS.
- * If that works, delete them all by setting the delete on close and close.
- */
-
-static NTSTATUS open_streams_for_delete(connection_struct *conn,
- const char *fname)
-{
- struct stream_struct *stream_info;
- files_struct **streams;
- int i;
- unsigned int num_streams;
- TALLOC_CTX *frame = talloc_stackframe();
- NTSTATUS status;
-
- status = SMB_VFS_STREAMINFO(conn, NULL, fname, talloc_tos(),
- &num_streams, &stream_info);
-
- if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)
- || NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
- DEBUG(10, ("no streams around\n"));
- TALLOC_FREE(frame);
- return NT_STATUS_OK;
- }
-
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(10, ("SMB_VFS_STREAMINFO failed: %s\n",
- nt_errstr(status)));
- goto fail;
- }
-
- DEBUG(10, ("open_streams_for_delete found %d streams\n",
- num_streams));
-
- if (num_streams == 0) {
- TALLOC_FREE(frame);
- return NT_STATUS_OK;
- }
-
- streams = TALLOC_ARRAY(talloc_tos(), files_struct *, num_streams);
- if (streams == NULL) {
- DEBUG(0, ("talloc failed\n"));
- status = NT_STATUS_NO_MEMORY;
- goto fail;
- }
-
- for (i=0; i<num_streams; i++) {
- char *streamname;
-
- if (strequal(stream_info[i].name, "::$DATA")) {
- streams[i] = NULL;
- continue;
- }
-
- streamname = talloc_asprintf(talloc_tos(), "%s%s", fname,
- stream_info[i].name);
-
- if (streamname == NULL) {
- DEBUG(0, ("talloc_aprintf failed\n"));
- status = NT_STATUS_NO_MEMORY;
- goto fail;
- }
-
- status = onefs_create_file_unixpath
- (conn, /* conn */
- NULL, /* req */
- streamname, /* fname */
- DELETE_ACCESS, /* access_mask */
- FILE_SHARE_READ | FILE_SHARE_WRITE
- | FILE_SHARE_DELETE, /* share_access */
- FILE_OPEN, /* create_disposition*/
- NTCREATEX_OPTIONS_PRIVATE_STREAM_DELETE, /* create_options */
- FILE_ATTRIBUTE_NORMAL, /* file_attributes */
- 0, /* oplock_request */
- 0, /* allocation_size */
- NULL, /* sd */
- NULL, /* ea_list */
- &streams[i], /* result */
- NULL, /* pinfo */
- NULL); /* psbuf */
-
- TALLOC_FREE(streamname);
-
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(10, ("Could not open stream %s: %s\n",
- streamname, nt_errstr(status)));
- break;
- }
- }
-
- /*
- * don't touch the variable "status" beyond this point :-)
- */
-
- for (i -= 1 ; i >= 0; i--) {
- if (streams[i] == NULL) {
- continue;
- }
-
- DEBUG(10, ("Closing stream # %d, %s\n", i,
- streams[i]->fsp_name));
- close_file(NULL, streams[i], NORMAL_CLOSE);
- }
-
- fail:
- TALLOC_FREE(frame);
- return status;
-}
-
/*
* Wrapper around onefs_open_file_ntcreate and onefs_open_directory.
*/
struct ea_list *ea_list,
files_struct **result,
int *pinfo,
+ struct onefs_fsp_data *fsp_data,
SMB_STRUCT_STAT *psbuf)
{
SMB_STRUCT_STAT sbuf;
}
if (req == NULL) {
+ SMB_ASSERT((oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK) ==
+ NO_OPLOCK);
oplock_request |= INTERNAL_OPEN_ONLY;
}
}
if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
- && is_ntfs_stream_name(fname)
- && (!(create_options & NTCREATEX_OPTIONS_PRIVATE_STREAM_DELETE))) {
+ && is_ntfs_stream_name(fname)) {
char *base;
uint32 base_create_disposition;
goto fail;
}
- status = split_ntfs_stream_name(talloc_tos(), fname,
- &base, NULL);
+ status = onefs_split_ntfs_stream_name(talloc_tos(), fname,
+ &base, NULL);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(10, ("onefs_create_file_unixpath: "
"split_ntfs_stream_name failed: %s\n",
conn, /* conn */
NULL, /* req */
base, /* fname */
- 0, /* access_mask */
+ SYNCHRONIZE_ACCESS, /* access_mask */
(FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE), /* share_access */
base_create_disposition, /* create_disposition*/
0, /* create_options */
- 0, /* file_attributes */
+ file_attributes, /* file_attributes */
NO_OPLOCK, /* oplock_request */
0, /* allocation_size */
NULL, /* sd */
NULL, /* ea_list */
&base_fsp, /* result */
NULL, /* pinfo */
+ NULL, /* fsp_data */
NULL); /* psbuf */
if (!NT_STATUS_IS_OK(status)) {
"failed: %s\n", base, nt_errstr(status)));
goto fail;
}
- /*
- * we don't need to low level fd: This might conflict with
- * OneFS streams.
- */
- fd_close(base_fsp);
}
/* Covert generic bits in the security descriptor. */
sd, /* sd */
fsp, /* result */
&info, /* pinfo */
+ fsp_data, /* fsp_data */
&sbuf); /* psbuf */
if(!NT_STATUS_IS_OK(status)) {
return status;
}
+static void destroy_onefs_fsp_data(void *p_data)
+{
+ struct onefs_fsp_data *fsp_data = (struct onefs_fsp_data *)p_data;
+
+ destroy_onefs_callback_record(fsp_data->oplock_callback_id);
+}
+
/**
* SMB_VFS_CREATE_FILE interface to onefs.
*/
{
connection_struct *conn = handle->conn;
struct case_semantics_state *case_state = NULL;
+ struct onefs_fsp_data fsp_data = {};
SMB_STRUCT_STAT sbuf;
int info = FILE_WAS_OPENED;
files_struct *fsp = NULL;
ea_list, /* ea_list */
&fsp, /* result */
&info, /* pinfo */
+ &fsp_data, /* fsp_data */
&sbuf); /* psbuf */
if (!NT_STATUS_IS_OK(status)) {
DEBUG(10, ("onefs_create_file: info=%d\n", info));
+ /*
+ * Setup private onefs_fsp_data. Currently the private data struct is
+ * only used to store the oplock_callback_id so that when the file is
+ * closed, the onefs_callback_record can be properly cleaned up in the
+ * oplock_onefs sub-system.
+ */
+ if (fsp) {
+ struct onefs_fsp_data *fsp_data_tmp = NULL;
+ fsp_data_tmp = (struct onefs_fsp_data *)
+ VFS_ADD_FSP_EXTENSION(handle, fsp, struct onefs_fsp_data,
+ &destroy_onefs_fsp_data);
+
+ if (fsp_data_tmp == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ *fsp_data_tmp = fsp_data;
+ }
+
*result = fsp;
if (pinfo != NULL) {
*pinfo = info;