#include "vfs_posix.h"
#include "system/dir.h"
#include "system/time.h"
-#include "lib/util/dlinklist.h"
+#include "../lib/util/dlinklist.h"
#include "messaging/messaging.h"
#include "librpc/gen_ndr/xattr.h"
*/
static int pvfs_dir_handle_destructor(struct pvfs_file_handle *h)
{
- int open_count;
- char *path = NULL;
-
- if (h->name->stream_name == NULL &&
- pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
- open_count == 1) {
- NTSTATUS status;
- status = pvfs_xattr_unlink_hook(h->pvfs, path);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
- path, nt_errstr(status)));
- }
- if (rmdir(path) != 0) {
- DEBUG(0,("pvfs_dir_handle_destructor: failed to rmdir '%s' - %s\n",
- path, strerror(errno)));
- }
- }
-
- talloc_free(path);
-
if (h->have_opendb_entry) {
struct odb_lock *lck;
NTSTATUS status;
+ const char *delete_path = NULL;
lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
if (lck == NULL) {
return 0;
}
- status = odb_close_file(lck, h);
+ status = odb_close_file(lck, h, &delete_path);
if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
+ DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
h->name->full_name, nt_errstr(status)));
}
+ if (h->name->stream_name == NULL && delete_path) {
+ status = pvfs_xattr_unlink_hook(h->pvfs, delete_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
+ delete_path, nt_errstr(status)));
+ }
+ if (rmdir(delete_path) != 0) {
+ DEBUG(0,("pvfs_dir_handle_destructor: failed to rmdir '%s' - %s\n",
+ delete_path, strerror(errno)));
+ }
+ }
+
talloc_free(lck);
}
form the lock context used for opendb locking. Note that we must
zero here to take account of possible padding on some architectures
*/
-static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
- TALLOC_CTX *mem_ctx, DATA_BLOB *key)
+NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
+ TALLOC_CTX *mem_ctx, DATA_BLOB *key)
{
struct {
dev_t device;
bool del_on_close;
uint32_t create_options;
uint32_t share_access;
+ bool forced;
create_options = io->generic.in.create_options;
share_access = io->generic.in.share_access;
+ forced = (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)?true:false;
+
if (name->stream_name) {
- return NT_STATUS_NOT_A_DIRECTORY;
+ if (forced) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ } else {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
}
/* if the client says it must be a directory, and it isn't,
return NT_STATUS_NOT_A_DIRECTORY;
}
+ /* found with gentest */
+ if (io->ntcreatex.in.access_mask == SEC_FLAG_MAXIMUM_ALLOWED &&
+ (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) &&
+ (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
switch (io->generic.in.open_disposition) {
case NTCREATEX_DISP_OPEN_IF:
break;
} else {
status = pvfs_access_check_create(pvfs, req, name, &access_mask);
}
- if (!NT_STATUS_IS_OK(status)) {
- return status;
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (io->generic.in.query_maximal_access) {
+ status = pvfs_access_maximal_allowed(pvfs, req, name,
+ &io->generic.out.maximal_access);
+ NT_STATUS_NOT_OK_RETURN(status);
}
f->ntvfs = h;
f->handle->seek_offset = 0;
f->handle->position = 0;
f->handle->mode = 0;
- f->handle->sticky_write_time = false;
+ f->handle->oplock = NULL;
+ ZERO_STRUCT(f->handle->write_time);
f->handle->open_completed = false;
if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
}
/* see if we are allowed to open at the same time as existing opens */
- status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
- share_access, access_mask, del_on_close,
- name->full_name, OPLOCK_NONE, NULL);
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, del_on_close,
+ io->generic.in.open_disposition, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+
+ /* now really mark the file as open */
+ status = odb_open_file(lck, f->handle, name->full_name,
+ NULL, name->dos.write_time,
+ false, OPLOCK_NONE, NULL);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(lck);
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
- status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
- share_access, access_mask, del_on_close,
- name->full_name, OPLOCK_NONE, NULL);
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, del_on_close,
+ io->generic.in.open_disposition, false);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ status = odb_open_file(lck, f->handle, name->full_name,
+ NULL, name->dos.write_time,
+ false, OPLOCK_NONE, NULL);
if (!NT_STATUS_IS_OK(status)) {
goto cleanup_delete;
*/
static int pvfs_handle_destructor(struct pvfs_file_handle *h)
{
- int open_count;
- char *path = NULL;
+ talloc_free(h->write_time.update_event);
+ h->write_time.update_event = NULL;
- /* the write time is no longer sticky */
- if (h->sticky_write_time) {
- NTSTATUS status;
- status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
- if (NT_STATUS_IS_OK(status)) {
- h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
- pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
- }
- }
-
if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
h->name->stream_name) {
NTSTATUS status;
h->fd = -1;
}
- if (h->name->stream_name == NULL &&
- h->open_completed &&
- pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
- open_count == 1) {
- NTSTATUS status;
- status = pvfs_xattr_unlink_hook(h->pvfs, path);
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
- path, nt_errstr(status)));
- }
- if (unlink(path) != 0) {
- DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
- path, strerror(errno)));
- } else {
- notify_trigger(h->pvfs->notify_context,
- NOTIFY_ACTION_REMOVED,
- FILE_NOTIFY_CHANGE_FILE_NAME,
- path);
- }
+ if (!h->write_time.update_forced &&
+ h->write_time.update_on_close &&
+ h->write_time.close_time == 0) {
+ struct timeval tv;
+ tv = timeval_current();
+ h->write_time.close_time = timeval_to_nttime(&tv);
}
- talloc_free(path);
-
if (h->have_opendb_entry) {
struct odb_lock *lck;
NTSTATUS status;
+ const char *delete_path = NULL;
lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
if (lck == NULL) {
return 0;
}
- status = odb_close_file(lck, h);
+ if (h->write_time.update_forced) {
+ status = odb_get_file_infos(h->pvfs->odb_context,
+ &h->odb_locking_key,
+ NULL,
+ &h->write_time.close_time);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable get write time for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ }
+
+ h->write_time.update_forced = false;
+ h->write_time.update_on_close = true;
+ } else if (h->write_time.update_on_close) {
+ status = odb_set_write_time(lck, h->write_time.close_time, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable set write time for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ }
+ }
+
+ status = odb_close_file(lck, h, &delete_path);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
h->name->full_name, nt_errstr(status)));
}
+ if (h->name->stream_name == NULL &&
+ h->open_completed && delete_path) {
+ status = pvfs_xattr_unlink_hook(h->pvfs, delete_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
+ delete_path, nt_errstr(status)));
+ }
+ if (unlink(delete_path) != 0) {
+ DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
+ delete_path, strerror(errno)));
+ } else {
+ notify_trigger(h->pvfs->notify_context,
+ NOTIFY_ACTION_REMOVED,
+ FILE_NOTIFY_CHANGE_FILE_NAME,
+ delete_path);
+ }
+ h->write_time.update_on_close = false;
+ }
+
talloc_free(lck);
}
+ if (h->write_time.update_on_close) {
+ struct timeval tv[2];
+
+ nttime_to_timeval(&tv[0], h->name->dos.access_time);
+ nttime_to_timeval(&tv[1], h->write_time.close_time);
+
+ if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
+ if (utimes(h->name->full_name, tv) == -1) {
+ DEBUG(0,("pvfs_handle_destructor: utimes() failed '%s' - %s\n",
+ h->name->full_name, strerror(errno)));
+ }
+ }
+ }
+
return 0;
}
bool del_on_close;
struct pvfs_filename *parent;
uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
+ bool allow_level_II_oplock = false;
+ if (io->ntcreatex.in.file_attr & ~FILE_ATTRIBUTE_ALL_MASK) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_ENCRYPTED) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
(create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
return NT_STATUS_CANNOT_DELETE;
}
-
+
status = pvfs_access_check_create(pvfs, req, name, &access_mask);
NT_STATUS_NOT_OK_RETURN(status);
DATA_BLOB locking_key;
status = pvfs_locking_key(parent, req, &locking_key);
NT_STATUS_NOT_OK_RETURN(status);
- status = odb_get_delete_on_close(pvfs->odb_context, &locking_key,
- &del_on_close, NULL, NULL);
+ status = odb_get_file_infos(pvfs->odb_context, &locking_key,
+ &del_on_close, NULL);
NT_STATUS_NOT_OK_RETURN(status);
if (del_on_close) {
return NT_STATUS_DELETE_PENDING;
mode = pvfs_fileperms(pvfs, attrib);
/* create the file */
- fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
+ fd = open(name->full_name, flags | O_CREAT | O_EXCL| O_NONBLOCK, mode);
if (fd == -1) {
return pvfs_map_errno(pvfs, errno);
}
}
/* re-resolve the open fd */
- status = pvfs_resolve_name_fd(pvfs, fd, name);
+ status = pvfs_resolve_name_fd(pvfs, fd, name, 0);
if (!NT_STATUS_IS_OK(status)) {
close(fd);
return status;
}
+ /* support initial alloc sizes */
+ name->dos.alloc_size = io->ntcreatex.in.alloc_size;
name->dos.attrib = attrib;
status = pvfs_dosattrib_save(pvfs, name, fd);
if (!NT_STATUS_IS_OK(status)) {
goto cleanup_delete;
}
+ if (io->generic.in.query_maximal_access) {
+ status = pvfs_access_maximal_allowed(pvfs, req, name,
+ &io->generic.out.maximal_access);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
/* form the lock context used for byte range locking and
opendb locking */
status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
oplock_level = OPLOCK_EXCLUSIVE;
}
- status = odb_open_file(lck, f->handle, name->stream_id,
- share_access, access_mask, del_on_close,
- name->full_name, oplock_level, &oplock_granted);
- talloc_free(lck);
+ if (req->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS) {
+ allow_level_II_oplock = true;
+ }
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, del_on_close,
+ io->generic.in.open_disposition, false);
if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
/* bad news, we must have hit a race - we don't delete the file
- here as the most likely scenario is that someone else created
+ here as the most likely scenario is that someone else created
the file at the same time */
close(fd);
return status;
}
- if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
- oplock_granted = OPLOCK_BATCH;
- }
-
f->ntvfs = h;
f->pvfs = pvfs;
f->pending_list = NULL;
f->handle->seek_offset = 0;
f->handle->position = 0;
f->handle->mode = 0;
+ f->handle->oplock = NULL;
f->handle->have_opendb_entry = true;
- f->handle->sticky_write_time = false;
+ ZERO_STRUCT(f->handle->write_time);
f->handle->open_completed = false;
+ status = odb_open_file(lck, f->handle, name->full_name,
+ &f->handle->fd, name->dos.write_time,
+ allow_level_II_oplock,
+ oplock_level, &oplock_granted);
+ talloc_free(lck);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* bad news, we must have hit a race - we don't delete the file
+ here as the most likely scenario is that someone else created
+ the file at the same time */
+ close(fd);
+ return status;
+ }
+
DLIST_ADD(pvfs->files.list, f);
/* setup a destructor to avoid file descriptor leaks on
talloc_set_destructor(f, pvfs_fnum_destructor);
talloc_set_destructor(f->handle, pvfs_handle_destructor);
+ if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
+ oplock_granted = OPLOCK_BATCH;
+ } else if (oplock_granted != OPLOCK_NONE) {
+ status = pvfs_setup_oplock(f, oplock_granted);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
io->generic.out.oplock_level = oplock_granted;
io->generic.out.file.ntvfs = f->ntvfs;
io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
return status;
}
-
/*
- state of a pending open retry
+ state of a pending retry
*/
-struct pvfs_open_retry {
+struct pvfs_odb_retry {
struct ntvfs_module_context *ntvfs;
struct ntvfs_request *req;
- union smb_open *io;
- struct pvfs_wait *wait_handle;
DATA_BLOB odb_locking_key;
+ void *io;
+ void *private_data;
+ void (*callback)(struct pvfs_odb_retry *r,
+ struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *io,
+ void *private_data,
+ enum pvfs_wait_notice reason);
};
-/* destroy a pending open request */
-static int pvfs_retry_destructor(struct pvfs_open_retry *r)
+/* destroy a pending request */
+static int pvfs_odb_retry_destructor(struct pvfs_odb_retry *r)
{
struct pvfs_state *pvfs = r->ntvfs->private_data;
if (r->odb_locking_key.data) {
return 0;
}
+static void pvfs_odb_retry_callback(void *_r, enum pvfs_wait_notice reason)
+{
+ struct pvfs_odb_retry *r = talloc_get_type(_r, struct pvfs_odb_retry);
+
+ if (reason == PVFS_WAIT_EVENT) {
+ /*
+ * The pending odb entry is already removed.
+ * We use a null locking key to indicate this
+ * to the destructor.
+ */
+ data_blob_free(&r->odb_locking_key);
+ }
+
+ r->callback(r, r->ntvfs, r->req, r->io, r->private_data, reason);
+}
+
/*
- retry an open
+ setup for a retry of a request that was rejected
+ by odb_can_open()
*/
-static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
+NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct odb_lock *lck,
+ struct timeval end_time,
+ void *io,
+ void *private_data,
+ void (*callback)(struct pvfs_odb_retry *r,
+ struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *io,
+ void *private_data,
+ enum pvfs_wait_notice reason))
{
- struct pvfs_open_retry *r = private;
- struct ntvfs_module_context *ntvfs = r->ntvfs;
- struct ntvfs_request *req = r->req;
- union smb_open *io = r->io;
+ struct pvfs_state *pvfs = ntvfs->private_data;
+ struct pvfs_odb_retry *r;
+ struct pvfs_wait *wait_handle;
NTSTATUS status;
+ r = talloc(req, struct pvfs_odb_retry);
+ NT_STATUS_HAVE_NO_MEMORY(r);
+
+ r->ntvfs = ntvfs;
+ r->req = req;
+ r->io = io;
+ r->private_data = private_data;
+ r->callback = callback;
+ r->odb_locking_key = odb_get_key(r, lck);
+ if (r->odb_locking_key.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* setup a pending lock */
+ status = odb_open_file_pending(lck, r);
+ if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND,status)) {
+ /*
+ * maybe only a unix application
+ * has the file open
+ */
+ data_blob_free(&r->odb_locking_key);
+ } else if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ talloc_free(lck);
+
+ talloc_set_destructor(r, pvfs_odb_retry_destructor);
+
+ wait_handle = pvfs_wait_message(pvfs, req,
+ MSG_PVFS_RETRY_OPEN, end_time,
+ pvfs_odb_retry_callback, r);
+ if (wait_handle == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ talloc_steal(r, wait_handle);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ retry an open after a sharing violation
+*/
+static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
+ struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *_io,
+ void *private_data,
+ enum pvfs_wait_notice reason)
+{
+ union smb_open *io = talloc_get_type(_io, union smb_open);
+ struct timeval *final_timeout = NULL;
+ NTSTATUS status;
+
+ if (private_data) {
+ final_timeout = talloc_get_type(private_data,
+ struct timeval);
+ }
+
/* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
just a bug in their server, but we better do the same */
if (reason == PVFS_WAIT_CANCEL) {
return;
}
- talloc_free(r->wait_handle);
-
if (reason == PVFS_WAIT_TIMEOUT) {
+ if (final_timeout &&
+ !timeval_expired(final_timeout)) {
+ /*
+ * we need to retry periodictly
+ * after an EAGAIN as there's
+ * no way the kernel tell us
+ * an oplock is released.
+ */
+ goto retry;
+ }
/* if it timed out, then give the failure
immediately */
talloc_free(r);
return;
}
- /* the pending odb entry is already removed. We use a null locking
- key to indicate this */
- data_blob_free(&r->odb_locking_key);
+retry:
talloc_free(r);
/* try the open again, which could trigger another retry setup
struct ntvfs_request *req,
union smb_open *io,
struct pvfs_file *f,
- struct odb_lock *lck)
+ struct odb_lock *lck,
+ NTSTATUS parent_status)
{
struct pvfs_state *pvfs = ntvfs->private_data;
- struct pvfs_open_retry *r;
NTSTATUS status;
struct timeval end_time;
+ struct timeval *final_timeout = NULL;
if (io->generic.in.create_options &
(NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
}
}
- r = talloc(req, struct pvfs_open_retry);
- if (r == NULL) {
- return NT_STATUS_NO_MEMORY;
- }
-
- r->ntvfs = ntvfs;
- r->req = req;
- r->io = io;
- r->odb_locking_key = data_blob_talloc(r,
- f->handle->odb_locking_key.data,
- f->handle->odb_locking_key.length);
-
- end_time = timeval_add(&req->statistics.request_time, 0, pvfs->sharing_violation_delay);
-
- /* setup a pending lock */
- status = odb_open_file_pending(lck, r);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
- }
-
- talloc_free(lck);
+ /* the retry should allocate a new file handle */
talloc_free(f);
- talloc_set_destructor(r, pvfs_retry_destructor);
-
- r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
- pvfs_open_retry, r);
- if (r->wait_handle == NULL) {
- return NT_STATUS_NO_MEMORY;
+ if (NT_STATUS_EQUAL(parent_status, NT_STATUS_SHARING_VIOLATION)) {
+ end_time = timeval_add(&req->statistics.request_time,
+ 0, pvfs->sharing_violation_delay);
+ } else if (NT_STATUS_EQUAL(parent_status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+ end_time = timeval_add(&req->statistics.request_time,
+ pvfs->oplock_break_timeout, 0);
+ } else if (NT_STATUS_EQUAL(parent_status, STATUS_MORE_ENTRIES)) {
+ /*
+ * we got EAGAIN which means a unix application
+ * has an oplock or share mode
+ *
+ * we retry every 4/5 of the sharing violation delay
+ * to see if the unix application
+ * has released the oplock or share mode.
+ */
+ final_timeout = talloc(req, struct timeval);
+ NT_STATUS_HAVE_NO_MEMORY(final_timeout);
+ *final_timeout = timeval_add(&req->statistics.request_time,
+ pvfs->oplock_break_timeout,
+ 0);
+ end_time = timeval_current_ofs(0, (pvfs->sharing_violation_delay*4)/5);
+ end_time = timeval_min(final_timeout, &end_time);
+ } else {
+ return NT_STATUS_INTERNAL_ERROR;
}
- talloc_steal(pvfs, r);
-
- return NT_STATUS_OK;
+ return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io,
+ final_timeout, pvfs_retry_open_sharing);
}
/*
struct ntvfs_request *req, union smb_open *io)
{
struct pvfs_state *pvfs = ntvfs->private_data;
- int flags;
+ int flags = 0;
struct pvfs_filename *name;
struct pvfs_file *f;
struct ntvfs_handle *h;
int fd;
struct odb_lock *lck;
uint32_t create_options;
+ uint32_t create_options_must_ignore_mask;
uint32_t share_access;
uint32_t access_mask;
+ uint32_t create_action = NTCREATEX_ACTION_EXISTED;
bool del_on_close;
bool stream_existed, stream_truncate=false;
uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
+ bool allow_level_II_oplock = false;
/* use the generic mapping code to avoid implementing all the
different open calls. */
return ntvfs_map_open(ntvfs, req, io);
}
+ ZERO_STRUCT(io->generic.out);
+
+ create_options = io->generic.in.create_options;
+ share_access = io->generic.in.share_access;
+ access_mask = io->generic.in.access_mask;
+
+ if (share_access & ~NTCREATEX_SHARE_ACCESS_MASK) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * These options are ignored,
+ * but we reuse some of them as private values for the generic mapping
+ */
+ create_options_must_ignore_mask = NTCREATEX_OPTIONS_MUST_IGNORE_MASK;
+ create_options_must_ignore_mask &= ~NTCREATEX_OPTIONS_PRIVATE_MASK;
+ create_options &= ~create_options_must_ignore_mask;
+
+ if (create_options & NTCREATEX_OPTIONS_NOT_SUPPORTED_MASK) {
+ DEBUG(2,(__location__ " create_options 0x%x not supported\n",
+ create_options));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (create_options & NTCREATEX_OPTIONS_INVALID_PARAM_MASK) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* TODO: When we implement HSM, add a hook here not to pull
+ * the actual file off tape, when this option is passed from
+ * the client */
+ if (create_options & NTCREATEX_OPTIONS_NO_RECALL) {
+ /* no-op */
+ }
+
+ /* TODO: If (unlikely) Linux does a good compressed
+ * filesystem, we might need an ioctl call for this */
+ if (create_options & NTCREATEX_OPTIONS_NO_COMPRESSION) {
+ /* no-op */
+ }
+
+ if (create_options & NTCREATEX_OPTIONS_NO_INTERMEDIATE_BUFFERING) {
+ create_options |= NTCREATEX_OPTIONS_WRITE_THROUGH;
+ }
+
+ /* Open the file with sync, if they asked for it, but
+ 'strict sync = no' turns this client request into a no-op */
+ if (create_options & (NTCREATEX_OPTIONS_WRITE_THROUGH) && !(pvfs->flags | PVFS_FLAG_STRICT_SYNC)) {
+ flags |= O_SYNC;
+ }
+
+
+ /* other create options are not allowed */
+ if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
+ !(access_mask & SEC_STD_DELETE)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (access_mask & SEC_MASK_INVALID) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* what does this bit really mean?? */
+ if (req->ctx->protocol == PROTOCOL_SMB2 &&
+ access_mask == SEC_STD_SYNCHRONIZE) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (io->ntcreatex.in.file_attr & (FILE_ATTRIBUTE_DEVICE|
+ FILE_ATTRIBUTE_VOLUME|
+ (~FILE_ATTRIBUTE_ALL_MASK))) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* we ignore some file_attr bits */
+ io->ntcreatex.in.file_attr &= ~(FILE_ATTRIBUTE_NONINDEXED |
+ FILE_ATTRIBUTE_COMPRESSED |
+ FILE_ATTRIBUTE_REPARSE_POINT |
+ FILE_ATTRIBUTE_SPARSE |
+ FILE_ATTRIBUTE_NORMAL);
+
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
PVFS_RESOLVE_STREAMS, &name);
return status;
}
+ /* if the client specified that it must not be a directory then
+ check that it isn't */
+ if (name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+ (io->generic.in.create_options & NTCREATEX_OPTIONS_NON_DIRECTORY_FILE)) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ /* if the client specified that it must be a directory then
+ check that it is */
+ if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+ (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+
/* directory opens are handled separately */
if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
(io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
open doesn't match */
io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
- create_options = io->generic.in.create_options;
- share_access = io->generic.in.share_access;
- access_mask = io->generic.in.access_mask;
-
- /* certain create options are not allowed */
- if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
- !(access_mask & SEC_STD_DELETE)) {
- return NT_STATUS_INVALID_PARAMETER;
- }
-
- flags = 0;
-
switch (io->generic.in.open_disposition) {
case NTCREATEX_DISP_SUPERSEDE:
case NTCREATEX_DISP_OVERWRITE_IF:
} else {
stream_truncate = true;
}
+ create_action = NTCREATEX_ACTION_TRUNCATED;
break;
case NTCREATEX_DISP_OPEN:
} else {
stream_truncate = true;
}
+ create_action = NTCREATEX_ACTION_TRUNCATED;
break;
case NTCREATEX_DISP_CREATE:
/* check the security descriptor */
status = pvfs_access_check(pvfs, req, name, &access_mask);
- if (!NT_STATUS_IS_OK(status)) {
- return status;
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (io->generic.in.query_maximal_access) {
+ status = pvfs_access_maximal_allowed(pvfs, req, name,
+ &io->generic.out.maximal_access);
+ NT_STATUS_NOT_OK_RETURN(status);
}
status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
f->handle->seek_offset = 0;
f->handle->position = 0;
f->handle->mode = 0;
+ f->handle->oplock = NULL;
f->handle->have_opendb_entry = false;
- f->handle->sticky_write_time = false;
+ ZERO_STRUCT(f->handle->write_time);
f->handle->open_completed = false;
/* form the lock context used for byte range locking and
oplock_level = OPLOCK_EXCLUSIVE;
}
+ if (req->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS) {
+ allow_level_II_oplock = true;
+ }
+
/* see if we are allowed to open at the same time as existing opens */
- status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
- share_access, access_mask, del_on_close,
- name->full_name, oplock_level, &oplock_granted);
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, del_on_close,
+ io->generic.in.open_disposition, false);
- /* on a sharing violation we need to retry when the file is closed by
- the other user, or after 1 second */
- if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
+ /*
+ * on a sharing violation we need to retry when the file is closed by
+ * the other user, or after 1 second
+ * on a non granted oplock we need to retry when the file is closed by
+ * the other user, or after 30 seconds
+ */
+ if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
- return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
+ return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
}
if (!NT_STATUS_IS_OK(status)) {
return status;
}
- if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
- oplock_granted = OPLOCK_BATCH;
- }
-
- f->handle->have_opendb_entry = true;
-
if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
flags |= O_RDWR;
} else {
}
/* do the actual open */
- fd = open(f->handle->name->full_name, flags);
+ fd = open(f->handle->name->full_name, flags | O_NONBLOCK);
if (fd == -1) {
+ status = pvfs_map_errno(f->pvfs, errno);
+
+ /*
+ * STATUS_MORE_ENTRIES is EAGAIN or EWOULDBLOCK
+ */
+ if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) &&
+ (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
+ }
+
talloc_free(lck);
- return pvfs_map_errno(f->pvfs, errno);
+ return status;
}
f->handle->fd = fd;
+ /* now really mark the file as open */
+ status = odb_open_file(lck, f->handle, name->full_name,
+ &f->handle->fd, name->dos.write_time,
+ allow_level_II_oplock,
+ oplock_level, &oplock_granted);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+
+ f->handle->have_opendb_entry = true;
+
+ if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
+ oplock_granted = OPLOCK_BATCH;
+ } else if (oplock_granted != OPLOCK_NONE) {
+ status = pvfs_setup_oplock(f, oplock_granted);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+ }
+
stream_existed = name->stream_exists;
/* if this was a stream create then create the stream as well */
}
/* re-resolve the open fd */
- status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
+ status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name, PVFS_RESOLVE_NO_OPENDB);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(lck);
return status;
talloc_free(lck);
return pvfs_map_errno(pvfs, errno);
}
+ name->dos.alloc_size = io->ntcreatex.in.alloc_size;
name->dos.attrib = attrib;
status = pvfs_dosattrib_save(pvfs, name, fd);
if (!NT_STATUS_IS_OK(status)) {
io->generic.out.oplock_level = oplock_granted;
io->generic.out.file.ntvfs = h;
io->generic.out.create_action = stream_existed?
- NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
+ create_action:NTCREATEX_ACTION_CREATED;
+
io->generic.out.create_time = name->dos.create_time;
io->generic.out.access_time = name->dos.access_time;
io->generic.out.write_time = name->dos.write_time;
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_file *f;
- struct utimbuf unix_times;
if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
return NT_STATUS_DOS(ERRSRV, ERRerror);
}
- if (io->generic.level != RAW_CLOSE_CLOSE) {
+ if (io->generic.level != RAW_CLOSE_GENERIC) {
return ntvfs_map_close(ntvfs, req, io);
}
- f = pvfs_find_fd(pvfs, req, io->close.in.file.ntvfs);
+ f = pvfs_find_fd(pvfs, req, io->generic.in.file.ntvfs);
if (!f) {
return NT_STATUS_INVALID_HANDLE;
}
- if (!null_time(io->close.in.write_time)) {
- unix_times.actime = 0;
- unix_times.modtime = io->close.in.write_time;
- utime(f->handle->name->full_name, &unix_times);
- } else if (f->handle->sticky_write_time) {
- unix_times.actime = 0;
- unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
- utime(f->handle->name->full_name, &unix_times);
+ if (!null_time(io->generic.in.write_time)) {
+ f->handle->write_time.update_forced = false;
+ f->handle->write_time.update_on_close = true;
+ unix_to_nt_time(&f->handle->write_time.close_time, io->generic.in.write_time);
+ }
+
+ if (io->generic.in.flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) {
+ struct pvfs_filename *name;
+ NTSTATUS status;
+ struct pvfs_file_handle *h = f->handle;
+
+ status = pvfs_resolve_name_handle(pvfs, h);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ name = h->name;
+
+ io->generic.out.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
+ io->generic.out.create_time = name->dos.create_time;
+ io->generic.out.access_time = name->dos.access_time;
+ io->generic.out.write_time = name->dos.write_time;
+ io->generic.out.change_time = name->dos.change_time;
+ io->generic.out.alloc_size = name->dos.alloc_size;
+ io->generic.out.size = name->st.st_size;
+ io->generic.out.file_attr = name->dos.attrib;
+ } else {
+ ZERO_STRUCT(io->generic.out);
}
talloc_free(f);
NTSTATUS status;
DATA_BLOB key;
struct odb_lock *lck;
+ uint32_t share_access;
+ uint32_t access_mask;
+ bool delete_on_close;
status = pvfs_locking_key(name, name, &key);
if (!NT_STATUS_IS_OK(status)) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
- status = odb_can_open(lck,
- NTCREATEX_SHARE_ACCESS_READ |
- NTCREATEX_SHARE_ACCESS_WRITE |
- NTCREATEX_SHARE_ACCESS_DELETE,
- NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
- SEC_STD_DELETE);
+ share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE |
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ access_mask = SEC_STD_DELETE;
+ delete_on_close = true;
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, delete_on_close,
+ NTCREATEX_DISP_OPEN, false);
if (NT_STATUS_IS_OK(status)) {
- status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
+ status = pvfs_access_check_simple(pvfs, req, name, access_mask);
}
/*
NTSTATUS status;
DATA_BLOB key;
struct odb_lock *lck;
+ uint32_t share_access;
+ uint32_t access_mask;
+ bool delete_on_close;
+
+ status = pvfs_locking_key(name, name, &key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lck = odb_lock(req, pvfs->odb_context, &key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for can_stat\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ access_mask = SEC_STD_DELETE;
+ delete_on_close = false;
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, delete_on_close,
+ NTCREATEX_DISP_OPEN, false);
+
+ /*
+ * if it's a sharing violation or we got no oplock
+ * only keep the lock if the caller requested access
+ * to the lock
+ */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
+ NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
+ if (lckp) {
+ *lckp = lck;
+ } else {
+ talloc_free(lck);
+ }
+ } else if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ if (lckp) {
+ *lckp = NULL;
+ }
+ } else if (lckp) {
+ *lckp = lck;
+ }
+
+ return status;
+}
+
+/*
+ determine if the file size of a file can be changed,
+ or if it is prevented by an already open file
+*/
+NTSTATUS pvfs_can_update_file_size(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ struct odb_lock **lckp)
+{
+ NTSTATUS status;
+ DATA_BLOB key;
+ struct odb_lock *lck;
+ uint32_t share_access;
+ uint32_t access_mask;
+ bool break_to_none;
+ bool delete_on_close;
status = pvfs_locking_key(name, name, &key);
if (!NT_STATUS_IS_OK(status)) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
- status = odb_can_open(lck,
- NTCREATEX_SHARE_ACCESS_READ |
- NTCREATEX_SHARE_ACCESS_WRITE,
- 0,
- SEC_STD_DELETE);
+ share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE |
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ /*
+ * I would have thought that we would need to pass
+ * SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA here too
+ *
+ * But you only need SEC_FILE_WRITE_ATTRIBUTE permissions
+ * to set the filesize.
+ *
+ * --metze
+ */
+ access_mask = SEC_FILE_WRITE_ATTRIBUTE;
+ delete_on_close = false;
+ break_to_none = true;
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, delete_on_close,
+ NTCREATEX_DISP_OPEN, break_to_none);
/*
* if it's a sharing violation or we got no oplock
NTSTATUS status;
DATA_BLOB key;
struct odb_lock *lck;
+ uint32_t share_access;
+ uint32_t access_mask;
+ bool delete_on_close;
status = pvfs_locking_key(name, name, &key);
if (!NT_STATUS_IS_OK(status)) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
- status = odb_can_open(lck,
- NTCREATEX_SHARE_ACCESS_READ |
- NTCREATEX_SHARE_ACCESS_WRITE,
- 0, 0);
+ share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ access_mask = SEC_FILE_READ_ATTRIBUTE;
+ delete_on_close = false;
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, delete_on_close,
+ NTCREATEX_DISP_OPEN, false);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(lck);
/*
determine if delete on close is set on
*/
-bool pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h,
- int *open_count, char **path)
+bool pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h)
{
NTSTATUS status;
bool del_on_close;
- status = odb_get_delete_on_close(pvfs->odb_context, &h->odb_locking_key,
- &del_on_close, open_count, path);
+ status = odb_get_file_infos(pvfs->odb_context, &h->odb_locking_key,
+ &del_on_close, NULL);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
return false;