*/
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;
}
/* see if we are allowed to open at the same time as existing opens */
- status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
- share_access, access_mask, del_on_close,
- io->generic.in.open_disposition,
- false, 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, 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, name->full_name, name->stream_id,
- share_access, access_mask, del_on_close,
- io->generic.in.open_disposition,
- false, 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, 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;
-
/* the write time is no longer sticky */
if (h->sticky_write_time) {
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);
- }
- }
-
- 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",
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);
+ }
+ }
+
talloc_free(lck);
}
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_READONLY) &&
(create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
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);
+ &del_on_close);
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);
}
oplock_level = OPLOCK_EXCLUSIVE;
}
- status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
- share_access, access_mask, del_on_close,
- io->generic.in.open_disposition,
- false, 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;
}
-
f->ntvfs = h;
f->pvfs = pvfs;
f->pending_list = NULL;
f->handle->sticky_write_time = false;
f->handle->open_completed = false;
+ status = odb_open_file(lck, f->handle, name->full_name,
+ &f->handle->fd, 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
+ abnormal termination */
+ 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)) {
- talloc_free(lck);
return status;
}
}
- /* setup a destructor to avoid file descriptor leaks on
- abnormal termination */
- talloc_set_destructor(f, pvfs_fnum_destructor);
- talloc_set_destructor(f->handle, pvfs_handle_destructor);
-
io->generic.out.oplock_level = oplock_granted;
io->generic.out.file.ntvfs = f->ntvfs;
io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
/*
setup for a retry of a request that was rejected
- by odb_open_file() or odb_can_open()
+ by odb_can_open()
*/
NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
/* setup a pending lock */
status = odb_open_file_pending(lck, r);
- if (!NT_STATUS_IS_OK(status)) {
+ 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_steal(r, wait_handle);
- talloc_steal(pvfs, r);
-
return NT_STATUS_OK;
}
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) {
}
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;
}
+retry:
talloc_free(r);
/* try the open again, which could trigger another retry setup
struct pvfs_state *pvfs = ntvfs->private_data;
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)) {
} 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;
}
- return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
- pvfs_retry_open_sharing);
+ return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io,
+ final_timeout, pvfs_retry_open_sharing);
}
/*
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. */
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, name->full_name, name->stream_id,
- share_access, access_mask, del_on_close,
- io->generic.in.open_disposition,
- false, 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
return status;
}
- 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;
- }
- }
-
- 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, allow_level_II_oplock,
+ oplock_level, &oplock_granted);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+
+ 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;
+ }
+ }
+
+ f->handle->have_opendb_entry = true;
+
stream_existed = name->stream_exists;
/* if this was a stream create then create the stream as well */
/*
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);
+ &del_on_close);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
return false;