diff options
Diffstat (limited to 'source4/ntvfs')
-rw-r--r-- | source4/ntvfs/common/opendb.c | 36 | ||||
-rw-r--r-- | source4/ntvfs/common/opendb.h | 20 | ||||
-rw-r--r-- | source4/ntvfs/common/opendb_tdb.c | 373 | ||||
-rw-r--r-- | source4/ntvfs/posix/config.mk | 1 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_lock.c | 16 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_notify.c | 8 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_open.c | 378 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_oplock.c | 239 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_qfileinfo.c | 2 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_rename.c | 11 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_setfileinfo.c | 120 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_unlink.c | 185 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_wait.c | 37 | ||||
-rw-r--r-- | source4/ntvfs/posix/pvfs_write.c | 5 | ||||
-rw-r--r-- | source4/ntvfs/posix/vfs_posix.c | 4 | ||||
-rw-r--r-- | source4/ntvfs/posix/vfs_posix.h | 24 |
16 files changed, 1166 insertions, 293 deletions
diff --git a/source4/ntvfs/common/opendb.c b/source4/ntvfs/common/opendb.c index 4826ca5c26..36144d0406 100644 --- a/source4/ntvfs/common/opendb.c +++ b/source4/ntvfs/common/opendb.c @@ -81,6 +81,10 @@ _PUBLIC_ struct odb_lock *odb_lock(TALLOC_CTX *mem_ctx, return ops->odb_lock(mem_ctx, odb, file_key); } +_PUBLIC_ DATA_BLOB odb_get_key(TALLOC_CTX *mem_ctx, struct odb_lock *lck) +{ + return ops->odb_get_key(mem_ctx, lck); +} /* register an open file in the open files database. This implements the share_access @@ -89,15 +93,16 @@ _PUBLIC_ struct odb_lock *odb_lock(TALLOC_CTX *mem_ctx, Note that the path is only used by the delete on close logic, not for comparing with other filenames */ -_PUBLIC_ NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle, - uint32_t stream_id, uint32_t share_access, +_PUBLIC_ NTSTATUS odb_open_file(struct odb_lock *lck, + void *file_handle, const char *path, + uint32_t stream_id, uint32_t share_access, uint32_t access_mask, bool delete_on_close, - const char *path, + uint32_t open_disposition, bool break_to_none, uint32_t oplock_level, uint32_t *oplock_granted) { - return ops->odb_open_file(lck, file_handle, stream_id, share_access, - access_mask, delete_on_close, path, oplock_level, - oplock_granted); + return ops->odb_open_file(lck, file_handle, path, stream_id, share_access, + access_mask, delete_on_close, open_disposition, + break_to_none, oplock_level, oplock_granted); } @@ -161,8 +166,21 @@ _PUBLIC_ NTSTATUS odb_get_delete_on_close(struct odb_context *odb, create_options and access_mask */ _PUBLIC_ NTSTATUS odb_can_open(struct odb_lock *lck, - uint32_t share_access, uint32_t create_options, - uint32_t access_mask) + uint32_t stream_id, uint32_t share_access, + uint32_t access_mask, bool delete_on_close, + uint32_t open_disposition, bool break_to_none) +{ + return ops->odb_can_open(lck, stream_id, share_access, access_mask, + delete_on_close, open_disposition, break_to_none); +} + +_PUBLIC_ NTSTATUS odb_update_oplock(struct odb_lock *lck, void *file_handle, + uint32_t oplock_level) +{ + return ops->odb_update_oplock(lck, file_handle, oplock_level); +} + +_PUBLIC_ NTSTATUS odb_break_oplocks(struct odb_lock *lck) { - return ops->odb_can_open(lck, share_access, create_options, access_mask); + return ops->odb_break_oplocks(lck); } diff --git a/source4/ntvfs/common/opendb.h b/source4/ntvfs/common/opendb.h index 231ae3d7de..9591bcf6b9 100644 --- a/source4/ntvfs/common/opendb.h +++ b/source4/ntvfs/common/opendb.h @@ -24,10 +24,12 @@ struct opendb_ops { struct ntvfs_context *ntvfs_ctx); struct odb_lock *(*odb_lock)(TALLOC_CTX *mem_ctx, struct odb_context *odb, DATA_BLOB *file_key); - NTSTATUS (*odb_open_file)(struct odb_lock *lck, void *file_handle, - uint32_t stream_id, uint32_t share_access, + DATA_BLOB (*odb_get_key)(TALLOC_CTX *mem_ctx, struct odb_lock *lck); + NTSTATUS (*odb_open_file)(struct odb_lock *lck, + void *file_handle, const char *path, + uint32_t stream_id, uint32_t share_access, uint32_t access_mask, bool delete_on_close, - const char *path, + uint32_t open_disposition, bool break_to_none, uint32_t oplock_level, uint32_t *oplock_granted); NTSTATUS (*odb_open_file_pending)(struct odb_lock *lck, void *private); NTSTATUS (*odb_close_file)(struct odb_lock *lck, void *file_handle); @@ -38,10 +40,18 @@ struct opendb_ops { DATA_BLOB *key, bool *del_on_close, int *open_count, char **path); NTSTATUS (*odb_can_open)(struct odb_lock *lck, - uint32_t share_access, uint32_t create_options, - uint32_t access_mask); + uint32_t stream_id, uint32_t share_access, + uint32_t access_mask, bool delete_on_close, + uint32_t open_disposition, bool break_to_none); + NTSTATUS (*odb_update_oplock)(struct odb_lock *lck, void *file_handle, + uint32_t oplock_level); + NTSTATUS (*odb_break_oplocks)(struct odb_lock *lck); }; +struct opendb_oplock_break { + void *file_handle; + uint8_t level; +}; void odb_set_ops(const struct opendb_ops *new_ops); void odb_tdb_init_ops(void); diff --git a/source4/ntvfs/common/opendb_tdb.c b/source4/ntvfs/common/opendb_tdb.c index abd9ca708b..fe5a0a8864 100644 --- a/source4/ntvfs/common/opendb_tdb.c +++ b/source4/ntvfs/common/opendb_tdb.c @@ -2,7 +2,8 @@ Unix SMB/CIFS implementation. Copyright (C) Andrew Tridgell 2004 - + Copyright (C) Stefan Metzmacher 2008 + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or @@ -134,12 +135,21 @@ static struct odb_lock *odb_tdb_lock(TALLOC_CTX *mem_ctx, return lck; } +static DATA_BLOB odb_tdb_get_key(TALLOC_CTX *mem_ctx, struct odb_lock *lck) +{ + return data_blob_talloc(mem_ctx, lck->key.dptr, lck->key.dsize); +} + + /* determine if two odb_entry structures conflict return NT_STATUS_OK on no conflict */ -static NTSTATUS share_conflict(struct opendb_entry *e1, struct opendb_entry *e2) +static NTSTATUS share_conflict(struct opendb_entry *e1, + uint32_t stream_id, + uint32_t share_access, + uint32_t access_mask) { /* if either open involves no read.write or delete access then it can't conflict */ @@ -150,18 +160,18 @@ static NTSTATUS share_conflict(struct opendb_entry *e1, struct opendb_entry *e2) SEC_STD_DELETE))) { return NT_STATUS_OK; } - if (!(e2->access_mask & (SEC_FILE_WRITE_DATA | - SEC_FILE_APPEND_DATA | - SEC_FILE_READ_DATA | - SEC_FILE_EXECUTE | - SEC_STD_DELETE))) { + if (!(access_mask & (SEC_FILE_WRITE_DATA | + SEC_FILE_APPEND_DATA | + SEC_FILE_READ_DATA | + SEC_FILE_EXECUTE | + SEC_STD_DELETE))) { return NT_STATUS_OK; } /* data IO access masks. This is skipped if the two open handles are on different streams (as in that case the masks don't interact) */ - if (e1->stream_id != e2->stream_id) { + if (e1->stream_id != stream_id) { return NT_STATUS_OK; } @@ -169,20 +179,20 @@ static NTSTATUS share_conflict(struct opendb_entry *e1, struct opendb_entry *e2) if (((am) & (right)) && !((sa) & (share))) return NT_STATUS_SHARING_VIOLATION CHECK_MASK(e1->access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA, - e2->share_access, NTCREATEX_SHARE_ACCESS_WRITE); - CHECK_MASK(e2->access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA, + share_access, NTCREATEX_SHARE_ACCESS_WRITE); + CHECK_MASK(access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA, e1->share_access, NTCREATEX_SHARE_ACCESS_WRITE); CHECK_MASK(e1->access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE, - e2->share_access, NTCREATEX_SHARE_ACCESS_READ); - CHECK_MASK(e2->access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE, + share_access, NTCREATEX_SHARE_ACCESS_READ); + CHECK_MASK(access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE, e1->share_access, NTCREATEX_SHARE_ACCESS_READ); CHECK_MASK(e1->access_mask, SEC_STD_DELETE, - e2->share_access, NTCREATEX_SHARE_ACCESS_DELETE); - CHECK_MASK(e2->access_mask, SEC_STD_DELETE, + share_access, NTCREATEX_SHARE_ACCESS_DELETE); + CHECK_MASK(access_mask, SEC_STD_DELETE, e1->share_access, NTCREATEX_SHARE_ACCESS_DELETE); - +#undef CHECK_MASK return NT_STATUS_OK; } @@ -253,12 +263,129 @@ static NTSTATUS odb_push_record(struct odb_lock *lck, struct opendb_file *file) /* send an oplock break to a client */ -static NTSTATUS odb_oplock_break_send(struct odb_context *odb, struct opendb_entry *e) +static NTSTATUS odb_oplock_break_send(struct odb_context *odb, + struct opendb_entry *e, + uint8_t level) { + NTSTATUS status; + struct opendb_oplock_break op_break; + DATA_BLOB blob; + + ZERO_STRUCT(op_break); + /* tell the server handling this open file about the need to send the client a break */ - return messaging_send_ptr(odb->ntvfs_ctx->msg_ctx, e->server, - MSG_NTVFS_OPLOCK_BREAK, e->file_handle); + op_break.file_handle = e->file_handle; + op_break.level = level; + + blob = data_blob_const(&op_break, sizeof(op_break)); + + status = messaging_send(odb->ntvfs_ctx->msg_ctx, e->server, + MSG_NTVFS_OPLOCK_BREAK, &blob); + NT_STATUS_NOT_OK_RETURN(status); + + return NT_STATUS_OK; +} + +static bool access_attributes_only(uint32_t access_mask, + uint32_t open_disposition, + bool break_to_none) +{ + switch (open_disposition) { + case NTCREATEX_DISP_SUPERSEDE: + case NTCREATEX_DISP_OVERWRITE_IF: + case NTCREATEX_DISP_OVERWRITE: + return false; + default: + break; + } + + if (break_to_none) { + return false; + } + +#define CHECK_MASK(m,g) ((m) && (((m) & ~(g))==0) && (((m) & (g)) != 0)) + return CHECK_MASK(access_mask, + SEC_STD_SYNCHRONIZE | + SEC_FILE_READ_ATTRIBUTE | + SEC_FILE_WRITE_ATTRIBUTE); +#undef CHECK_MASK +} + +static NTSTATUS odb_tdb_open_can_internal(struct odb_context *odb, + const struct opendb_file *file, + uint32_t stream_id, uint32_t share_access, + uint32_t access_mask, bool delete_on_close, + uint32_t open_disposition, bool break_to_none, + bool *_attrs_only) +{ + NTSTATUS status; + uint32_t i; + bool attrs_only = false; + + /* see if anyone has an oplock, which we need to break */ + for (i=0;i<file->num_entries;i++) { + if (file->entries[i].oplock_level == OPLOCK_BATCH) { + bool oplock_return = OPLOCK_BREAK_TO_LEVEL_II; + /* if this is an attribute only access + * it doesn't conflict with a BACTCH oplock + * but we'll not grant the oplock below + */ + attrs_only = access_attributes_only(access_mask, + open_disposition, + break_to_none); + if (attrs_only) { + break; + } + /* a batch oplock caches close calls, which + means the client application might have + already closed the file. We have to allow + this close to propogate by sending a oplock + break request and suspending this call + until the break is acknowledged or the file + is closed */ + if (break_to_none) { + oplock_return = OPLOCK_BREAK_TO_NONE; + } + odb_oplock_break_send(odb, &file->entries[i], + oplock_return); + return NT_STATUS_OPLOCK_NOT_GRANTED; + } + } + + if (file->delete_on_close) { + /* while delete on close is set, no new opens are allowed */ + return NT_STATUS_DELETE_PENDING; + } + + if (file->num_entries != 0 && delete_on_close) { + return NT_STATUS_SHARING_VIOLATION; + } + + /* check for sharing violations */ + for (i=0;i<file->num_entries;i++) { + status = share_conflict(&file->entries[i], stream_id, + share_access, access_mask); + NT_STATUS_NOT_OK_RETURN(status); + } + + /* we now know the open could succeed, but we need to check + for any exclusive oplocks. We can't grant a second open + till these are broken. Note that we check for batch oplocks + before checking for sharing violations, and check for + exclusive oplocks afterwards. */ + for (i=0;i<file->num_entries;i++) { + if (file->entries[i].oplock_level == OPLOCK_EXCLUSIVE) { + odb_oplock_break_send(odb, &file->entries[i], + OPLOCK_BREAK_TO_NONE); + return NT_STATUS_OPLOCK_NOT_GRANTED; + } + } + + if (_attrs_only) { + *_attrs_only = attrs_only; + } + return NT_STATUS_OK; } /* @@ -268,17 +395,18 @@ static NTSTATUS odb_oplock_break_send(struct odb_context *odb, struct opendb_ent Note that the path is only used by the delete on close logic, not for comparing with other filenames */ -static NTSTATUS odb_tdb_open_file(struct odb_lock *lck, void *file_handle, - uint32_t stream_id, uint32_t share_access, +static NTSTATUS odb_tdb_open_file(struct odb_lock *lck, + void *file_handle, const char *path, + uint32_t stream_id, uint32_t share_access, uint32_t access_mask, bool delete_on_close, - const char *path, + uint32_t open_disposition, bool break_to_none, uint32_t oplock_level, uint32_t *oplock_granted) { struct odb_context *odb = lck->odb; struct opendb_entry e; - int i; struct opendb_file file; NTSTATUS status; + bool attrs_only = false; if (odb->oplocks == false) { oplock_level = OPLOCK_NONE; @@ -294,6 +422,13 @@ static NTSTATUS odb_tdb_open_file(struct odb_lock *lck, void *file_handle, } /* see if it conflicts */ + status = odb_tdb_open_can_internal(odb, &file, stream_id, + share_access, access_mask, + delete_on_close, open_disposition, + break_to_none, &attrs_only); + NT_STATUS_NOT_OK_RETURN(status); + + /* see if it conflicts */ e.server = odb->ntvfs_ctx->server_id; e.file_handle = file_handle; e.stream_id = stream_id; @@ -301,59 +436,37 @@ static NTSTATUS odb_tdb_open_file(struct odb_lock *lck, void *file_handle, e.access_mask = access_mask; e.delete_on_close = delete_on_close; e.oplock_level = OPLOCK_NONE; - - /* see if anyone has an oplock, which we need to break */ - for (i=0;i<file.num_entries;i++) { - if (file.entries[i].oplock_level == OPLOCK_BATCH) { - /* a batch oplock caches close calls, which - means the client application might have - already closed the file. We have to allow - this close to propogate by sending a oplock - break request and suspending this call - until the break is acknowledged or the file - is closed */ - odb_oplock_break_send(odb, &file.entries[i]); - return NT_STATUS_OPLOCK_NOT_GRANTED; - } - } - - if (file.delete_on_close || - (file.num_entries != 0 && delete_on_close)) { - /* while delete on close is set, no new opens are allowed */ - return NT_STATUS_DELETE_PENDING; - } - - /* check for sharing violations */ - for (i=0;i<file.num_entries;i++) { - status = share_conflict(&file.entries[i], &e); - NT_STATUS_NOT_OK_RETURN(status); - } - - /* we now know the open could succeed, but we need to check - for any exclusive oplocks. We can't grant a second open - till these are broken. Note that we check for batch oplocks - before checking for sharing violations, and check for - exclusive oplocks afterwards. */ - for (i=0;i<file.num_entries;i++) { - if (file.entries[i].oplock_level == OPLOCK_EXCLUSIVE) { - odb_oplock_break_send(odb, &file.entries[i]); - return NT_STATUS_OPLOCK_NOT_GRANTED; - } - } /* - possibly grant an exclusive or batch oplock if this is the only client - with the file open. We don't yet grant levelII oplocks. + possibly grant an exclusive, batch or level2 oplock */ - if (oplock_granted != NULL) { - if ((oplock_level == OPLOCK_BATCH || - oplock_level == OPLOCK_EXCLUSIVE) && - file.num_entries == 0) { - (*oplock_granted) = oplock_level; + if (oplock_granted) { + if (attrs_only) { + e.oplock_level = OPLOCK_NONE; + *oplock_granted = NO_OPLOCK_RETURN; + } else if (oplock_level == OPLOCK_EXCLUSIVE) { + if (file.num_entries == 0) { + e.oplock_level = OPLOCK_EXCLUSIVE; + *oplock_granted = EXCLUSIVE_OPLOCK_RETURN; + } else { + e.oplock_level = OPLOCK_NONE; + *oplock_granted = NO_OPLOCK_RETURN; + } + } else if (oplock_level == OPLOCK_BATCH) { + if (file.num_entries == 0) { + e.oplock_level = OPLOCK_BATCH; + *oplock_granted = BATCH_OPLOCK_RETURN; + } else { + e.oplock_level = OPLOCK_LEVEL_II; + *oplock_granted = LEVEL_II_OPLOCK_RETURN; + } + } else if (oplock_level == OPLOCK_LEVEL_II) { + e.oplock_level = OPLOCK_LEVEL_II; + *oplock_granted = LEVEL_II_OPLOCK_RETURN; } else { - (*oplock_granted) = OPLOCK_NONE; + e.oplock_level = OPLOCK_NONE; + *oplock_granted = NO_OPLOCK_RETURN; } - e.oplock_level = (*oplock_granted); } /* it doesn't conflict, so add it to the end */ @@ -439,6 +552,82 @@ static NTSTATUS odb_tdb_close_file(struct odb_lock *lck, void *file_handle) return odb_push_record(lck, &file); } +/* + update the oplock level of the client +*/ +static NTSTATUS odb_tdb_update_oplock(struct odb_lock *lck, void *file_handle, + uint32_t oplock_level) +{ + struct odb_context *odb = lck->odb; + struct opendb_file file; + int i; + NTSTATUS status; + + status = odb_pull_record(lck, &file); + NT_STATUS_NOT_OK_RETURN(status); + + /* find the entry, and update it */ + for (i=0;i<file.num_entries;i++) { + if (file_handle == file.entries[i].file_handle && + cluster_id_equal(&odb->ntvfs_ctx->server_id, &file.entries[i].server)) { + file.entries[i].oplock_level = oplock_level; + break; + } + } + + if (i == file.num_entries) { + return NT_STATUS_UNSUCCESSFUL; + } + + /* send any pending notifications, removing them once sent */ + for (i=0;i<file.num_pending;i++) { + messaging_send_ptr(odb->ntvfs_ctx->msg_ctx, + file.pending[i].server, + MSG_PVFS_RETRY_OPEN, + file.pending[i].notify_ptr); + } + file.num_pending = 0; + + return odb_push_record(lck, &file); +} + +/* + send oplocks breaks to none to all level2 holders +*/ +static NTSTATUS odb_tdb_break_oplocks(struct odb_lock *lck) +{ + struct odb_context *odb = lck->odb; + NTSTATUS status; + struct opendb_file file; + int i; + bool modified = true; + + status = odb_pull_record(lck, &file); + if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + return NT_STATUS_OK; + } + NT_STATUS_NOT_OK_RETURN(status); + + /* see if anyone has an oplock, which we need to break */ + for (i=0;i<file.num_entries;i++) { + if (file.entries[i].oplock_level == OPLOCK_LEVEL_II) { + /* + * there could be multiple level2 oplocks + * and we just send a break to none to all of them + * without waiting for a release + */ + odb_oplock_break_send(odb, &file.entries[i], + OPLOCK_BREAK_TO_NONE); + file.entries[i].oplock_level = OPLOCK_NONE; + modified = true; + } + } + + if (modified) { + return odb_push_record(lck, &file); + } + return NT_STATUS_OK; +} /* remove a pending opendb entry @@ -560,14 +749,14 @@ static NTSTATUS odb_tdb_get_delete_on_close(struct odb_context *odb, create_options and access_mask */ static NTSTATUS odb_tdb_can_open(struct odb_lock *lck, - uint32_t share_access, uint32_t create_options, - uint32_t access_mask) + uint32_t stream_id, uint32_t share_access, + uint32_t access_mask, bool delete_on_close, + uint32_t open_disposition, bool break_to_none) { struct odb_context *odb = lck->odb; NTSTATUS status; struct opendb_file file; - struct opendb_entry e; - int i; + bool attrs_only = false; status = odb_pull_record(lck, &file); if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { @@ -575,32 +764,11 @@ static NTSTATUS odb_tdb_can_open(struct odb_lock *lck, } NT_STATUS_NOT_OK_RETURN(status); - if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) && - file.num_entries != 0) { - return NT_STATUS_SHARING_VIOLATION; - } - - if (file.delete_on_close) { - return NT_STATUS_DELETE_PENDING; - } - - e.server = odb->ntvfs_ctx->server_id; - e.file_handle = NULL; - e.stream_id = 0; - e.share_access = share_access; - e.access_mask = access_mask; - - for (i=0;i<file.num_entries;i++) { - status = share_conflict(&file.entries[i], &e); - if (!NT_STATUS_IS_OK(status)) { - /* note that we discard the error code - here. We do this as unless we are actually - doing an open (which comes via a different - function), we need to return a sharing - violation */ - return NT_STATUS_SHARING_VIOLATION; - } - } + status = odb_tdb_open_can_internal(odb, &file, stream_id, + share_access, access_mask, + delete_on_close, open_disposition, + break_to_none, &attrs_only); + NT_STATUS_NOT_OK_RETURN(status); return NT_STATUS_OK; } @@ -609,6 +777,7 @@ static NTSTATUS odb_tdb_can_open(struct odb_lock *lck, static const struct opendb_ops opendb_tdb_ops = { .odb_init = odb_tdb_init, .odb_lock = odb_tdb_lock, + .odb_get_key = odb_tdb_get_key, .odb_open_file = odb_tdb_open_file, .odb_open_file_pending = odb_tdb_open_file_pending, .odb_close_file = odb_tdb_close_file, @@ -616,7 +785,9 @@ static const struct opendb_ops opendb_tdb_ops = { .odb_rename = odb_tdb_rename, .odb_set_delete_on_close = odb_tdb_set_delete_on_close, .odb_get_delete_on_close = odb_tdb_get_delete_on_close, - .odb_can_open = odb_tdb_can_open + .odb_can_open = odb_tdb_can_open, + .odb_update_oplock = odb_tdb_update_oplock, + .odb_break_oplocks = odb_tdb_break_oplocks }; diff --git a/source4/ntvfs/posix/config.mk b/source4/ntvfs/posix/config.mk index 6879940337..88048c2af7 100644 --- a/source4/ntvfs/posix/config.mk +++ b/source4/ntvfs/posix/config.mk @@ -53,6 +53,7 @@ OBJ_FILES = \ pvfs_resolve.o \ pvfs_shortname.o \ pvfs_lock.o \ + pvfs_oplock.o \ pvfs_wait.o \ pvfs_seek.o \ pvfs_ioctl.o \ diff --git a/source4/ntvfs/posix/pvfs_lock.c b/source4/ntvfs/posix/pvfs_lock.c index b9bb58c71d..baa92880f1 100644 --- a/source4/ntvfs/posix/pvfs_lock.c +++ b/source4/ntvfs/posix/pvfs_lock.c @@ -53,7 +53,7 @@ struct pvfs_pending_lock { struct pvfs_file *f; struct ntvfs_request *req; int pending_lock; - void *wait_handle; + struct pvfs_wait *wait_handle; struct timeval end_time; }; @@ -294,6 +294,10 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs, return ntvfs_map_lock(ntvfs, req, lck); } + if (lck->lockx.in.mode & LOCKING_ANDX_OPLOCK_RELEASE) { + return pvfs_oplock_release(ntvfs, req, lck); + } + f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs); if (!f) { return NT_STATUS_INVALID_HANDLE; @@ -303,6 +307,9 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs, return NT_STATUS_FILE_IS_A_DIRECTORY; } + status = pvfs_break_level2_oplocks(f); + NT_STATUS_NOT_OK_RETURN(status); + if (lck->lockx.in.timeout != 0 && (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) { pending = talloc(f, struct pvfs_pending_lock); @@ -338,13 +345,6 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs, return NT_STATUS_DOS(ERRDOS, ERRnoatomiclocks); } - if (lck->lockx.in.mode & LOCKING_ANDX_OPLOCK_RELEASE) { - DEBUG(0,("received unexpected oplock break\n")); - talloc_free(pending); - return NT_STATUS_NOT_IMPLEMENTED; - } - - /* the unlocks happen first */ locks = lck->lockx.in.locks; diff --git a/source4/ntvfs/posix/pvfs_notify.c b/source4/ntvfs/posix/pvfs_notify.c index 210f949395..06d2bc8e0c 100644 --- a/source4/ntvfs/posix/pvfs_notify.c +++ b/source4/ntvfs/posix/pvfs_notify.c @@ -268,9 +268,11 @@ NTSTATUS pvfs_notify(struct ntvfs_module_context *ntvfs, /* if the buffer is empty then start waiting */ if (f->notify_buffer->num_changes == 0) { - void *wait_handle = - pvfs_wait_message(pvfs, req, -1, timeval_zero(), - pvfs_notify_end, f->notify_buffer); + struct pvfs_wait *wait_handle; + wait_handle = pvfs_wait_message(pvfs, req, -1, + timeval_zero(), + pvfs_notify_end, + f->notify_buffer); NT_STATUS_HAVE_NO_MEMORY(wait_handle); talloc_steal(req, wait_handle); return NT_STATUS_OK; diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c index 8558f0476a..a01352f60c 100644 --- a/source4/ntvfs/posix/pvfs_open.c +++ b/source4/ntvfs/posix/pvfs_open.c @@ -268,6 +268,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, f->handle->seek_offset = 0; f->handle->position = 0; f->handle->mode = 0; + f->handle->oplock = NULL; f->handle->sticky_write_time = false; f->handle->open_completed = false; @@ -296,9 +297,10 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, } /* 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, + status = odb_open_file(lck, f->handle, name->full_name, name->stream_id, share_access, access_mask, del_on_close, - name->full_name, OPLOCK_NONE, NULL); + io->generic.in.open_disposition, + false, OPLOCK_NONE, NULL); if (!NT_STATUS_IS_OK(status)) { talloc_free(lck); @@ -349,9 +351,10 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, return NT_STATUS_INTERNAL_DB_CORRUPTION; } - status = odb_open_file(lck, f->handle, f->handle->name->stream_id, + status = odb_open_file(lck, f->handle, name->full_name, name->stream_id, share_access, access_mask, del_on_close, - name->full_name, OPLOCK_NONE, NULL); + io->generic.in.open_disposition, + false, OPLOCK_NONE, NULL); if (!NT_STATUS_IS_OK(status)) { goto cleanup_delete; @@ -669,9 +672,10 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, oplock_level = OPLOCK_EXCLUSIVE; } - status = odb_open_file(lck, f->handle, name->stream_id, + status = odb_open_file(lck, f->handle, name->full_name, name->stream_id, share_access, access_mask, del_on_close, - name->full_name, oplock_level, &oplock_granted); + io->generic.in.open_disposition, + false, 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 @@ -681,9 +685,6 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, return status; } - if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) { - oplock_granted = OPLOCK_BATCH; - } f->ntvfs = h; f->pvfs = pvfs; @@ -702,12 +703,23 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, 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; f->handle->open_completed = false; DLIST_ADD(pvfs->files.list, f); + 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); @@ -748,20 +760,25 @@ cleanup_delete: 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; - void *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) { @@ -775,15 +792,92 @@ static int pvfs_retry_destructor(struct pvfs_open_retry *r) 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_open_file() or 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_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); + + talloc_steal(pvfs, r); + + 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); NTSTATUS status; /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably @@ -792,8 +886,6 @@ static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason) return; } - talloc_free(r->wait_handle); - if (reason == PVFS_WAIT_TIMEOUT) { /* if it timed out, then give the failure immediately */ @@ -803,9 +895,6 @@ static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason) return; } - /* the pending odb entry is already removed. We use a null locking - key to indicate this */ - data_blob_free(&r->odb_locking_key); talloc_free(r); /* try the open again, which could trigger another retry setup @@ -919,10 +1008,10 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs, 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; @@ -936,40 +1025,21 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs, } } - 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 { + return NT_STATUS_INTERNAL_ERROR; } - talloc_steal(pvfs, r); - - return NT_STATUS_OK; + return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL, + pvfs_retry_open_sharing); } /* @@ -1133,6 +1203,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, 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; f->handle->open_completed = false; @@ -1186,15 +1257,21 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, } /* 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, + status = odb_open_file(lck, f->handle, name->full_name, name->stream_id, share_access, access_mask, del_on_close, - name->full_name, oplock_level, &oplock_granted); + io->generic.in.open_disposition, + false, oplock_level, &oplock_granted); - /* 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)) { @@ -1204,6 +1281,12 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs, 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; @@ -1424,6 +1507,9 @@ NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs, 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)) { @@ -1436,21 +1522,38 @@ NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs, 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); } - if (!NT_STATUS_IS_OK(status)) { + /* + * 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); - *lckp = lck; - } else if (lckp != NULL) { + if (lckp) { + *lckp = NULL; + } + } else if (lckp) { *lckp = lck; } @@ -1469,6 +1572,9 @@ NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs, 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)) { @@ -1481,16 +1587,105 @@ NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs, 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; + access_mask = SEC_STD_DELETE; + delete_on_close = false; - if (!NT_STATUS_IS_OK(status)) { + 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; - } else if (lckp != NULL) { + } + + 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_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 | + 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 + * 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; } @@ -1508,6 +1703,9 @@ NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs, 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)) { @@ -1520,10 +1718,18 @@ NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs, 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); + } return status; } diff --git a/source4/ntvfs/posix/pvfs_oplock.c b/source4/ntvfs/posix/pvfs_oplock.c new file mode 100644 index 0000000000..cf30ddbc59 --- /dev/null +++ b/source4/ntvfs/posix/pvfs_oplock.c @@ -0,0 +1,239 @@ +/* + Unix SMB/CIFS implementation. + + POSIX NTVFS backend - oplock handling + + Copyright (C) Stefan Metzmacher 2008 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "includes.h" +#include "lib/messaging/messaging.h" +#include "lib/messaging/irpc.h" +#include "vfs_posix.h" + + +struct pvfs_oplock { + struct pvfs_file_handle *handle; + struct pvfs_file *file; + uint32_t level; + struct messaging_context *msg_ctx; +}; + +/* + receive oplock breaks and forward them to the client +*/ +static void pvfs_oplock_break(struct pvfs_oplock *opl, uint8_t level) +{ + NTSTATUS status; + struct pvfs_file *f = opl->file; + struct pvfs_file_handle *h = opl->handle; + struct pvfs_state *pvfs = h->pvfs; + + DEBUG(10,("pvfs_oplock_break: sending oplock break level %d for '%s' %p\n", + level, h->name->original_name, h)); + status = ntvfs_send_oplock_break(pvfs->ntvfs, f->ntvfs, level); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("pvfs_oplock_break: sending oplock break failed: %s\n", + nt_errstr(status))); + } +} + +static void pvfs_oplock_break_dispatch(struct messaging_context *msg, + void *private_data, uint32_t msg_type, + struct server_id src, DATA_BLOB *data) +{ + struct pvfs_oplock *opl = talloc_get_type(private_data, + struct pvfs_oplock); + struct opendb_oplock_break opb; + + ZERO_STRUCT(opb); + + /* we need to check that this one is for us. See + messaging_send_ptr() for the other side of this. + */ + if (data->length == sizeof(struct opendb_oplock_break)) { + struct opendb_oplock_break *p; + p = (struct opendb_oplock_break *)data->data; + opb = *p; + } else { + DEBUG(0,("%s: ignore oplock break with length[%u]\n", + __location__, data->length)); + return; + } + if (opb.file_handle != opl->handle) { + return; + } + + /* + * maybe we should use ntvfs_setup_async() + */ + pvfs_oplock_break(opl, opb.level); +} + +static int pvfs_oplock_destructor(struct pvfs_oplock *opl) +{ + messaging_deregister(opl->msg_ctx, MSG_NTVFS_OPLOCK_BREAK, opl); + return 0; +} + +NTSTATUS pvfs_setup_oplock(struct pvfs_file *f, uint32_t oplock_granted) +{ + NTSTATUS status; + struct pvfs_oplock *opl; + uint32_t level = OPLOCK_NONE; + + f->handle->oplock = NULL; + + switch (oplock_granted) { + case EXCLUSIVE_OPLOCK_RETURN: + level = OPLOCK_EXCLUSIVE; + break; + case BATCH_OPLOCK_RETURN: + level = OPLOCK_BATCH; + break; + case LEVEL_II_OPLOCK_RETURN: + level = OPLOCK_LEVEL_II; + break; + } + + if (level == OPLOCK_NONE) { + return NT_STATUS_OK; + } + + opl = talloc(f->handle, struct pvfs_oplock); + NT_STATUS_HAVE_NO_MEMORY(opl); + + opl->handle = f->handle; + opl->file = f; + opl->level = level; + opl->msg_ctx = f->pvfs->ntvfs->ctx->msg_ctx; + + status = messaging_register(opl->msg_ctx, + opl, + MSG_NTVFS_OPLOCK_BREAK, + pvfs_oplock_break_dispatch); + NT_STATUS_NOT_OK_RETURN(status); + + /* destructor */ + talloc_set_destructor(opl, pvfs_oplock_destructor); + + f->handle->oplock = opl; + + return NT_STATUS_OK; +} + +NTSTATUS pvfs_oplock_release(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, union smb_lock *lck) +{ + struct pvfs_state *pvfs = ntvfs->private_data; + struct pvfs_file *f; + struct pvfs_file_handle *h; + struct odb_lock *olck; + uint8_t oplock_break; + NTSTATUS status; + + f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs); + if (!f) { + return NT_STATUS_INVALID_HANDLE; + } + + h = f->handle; + + if (h->fd == -1) { + return NT_STATUS_FILE_IS_A_DIRECTORY; + } + + if (!h->have_opendb_entry) { + return NT_STATUS_FOOBAR; + } + + if (!h->oplock) { + return NT_STATUS_FOOBAR; + } + + olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key); + if (olck == NULL) { + DEBUG(0,("Unable to lock opendb for oplock update\n")); + return NT_STATUS_FOOBAR; + } + + oplock_break = (lck->lockx.in.mode >> 8) & 0xFF; + if (oplock_break == OPLOCK_BREAK_TO_NONE) { + h->oplock->level = OPLOCK_NONE; + } else if (oplock_break == OPLOCK_BREAK_TO_LEVEL_II) { + h->oplock->level = OPLOCK_LEVEL_II; + } else { + /* fallback to level II in case of a invalid value */ + DEBUG(1,("unexpected oplock break level[0x%02X]\n", oplock_break)); + h->oplock->level = OPLOCK_LEVEL_II; + } + status = odb_update_oplock(olck, h, h->oplock->level); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Unable to update oplock level for '%s' - %s\n", + h->name->full_name, nt_errstr(status))); + talloc_free(olck); + return status; + } + + talloc_free(olck); + + /* after a break to none, we no longer have an oplock attached */ + if (h->oplock->level == OPLOCK_NONE) { + talloc_free(h->oplock); + h->oplock = NULL; + } + + return NT_STATUS_OK; +} + +NTSTATUS pvfs_break_level2_oplocks(struct pvfs_file *f) +{ + struct pvfs_file_handle *h = f->handle; + struct odb_lock *olck; + NTSTATUS status; + + if (h->oplock && h->oplock->level == OPLOCK_EXCLUSIVE) { + return NT_STATUS_OK; + } + + olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key); + if (olck == NULL) { + DEBUG(0,("Unable to lock opendb for oplock update\n")); + return NT_STATUS_FOOBAR; + } + + if (h->oplock && h->oplock->level == OPLOCK_BATCH) { + status = odb_update_oplock(olck, h, OPLOCK_LEVEL_II); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Unable to update oplock level for '%s' - %s\n", + h->name->full_name, nt_errstr(status))); + talloc_free(olck); + return status; + } + } + + status = odb_break_oplocks(olck); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Unable to break level2 oplocks to none for '%s' - %s\n", + h->name->full_name, nt_errstr(status))); + talloc_free(olck); + return status; + } + + talloc_free(olck); + + return NT_STATUS_OK; +} diff --git a/source4/ntvfs/posix/pvfs_qfileinfo.c b/source4/ntvfs/posix/pvfs_qfileinfo.c index 2f01c08fb0..8d23d707a4 100644 --- a/source4/ntvfs/posix/pvfs_qfileinfo.c +++ b/source4/ntvfs/posix/pvfs_qfileinfo.c @@ -330,7 +330,7 @@ NTSTATUS pvfs_qpathinfo(struct ntvfs_module_context *ntvfs, status = pvfs_can_stat(pvfs, req, name); if (!NT_STATUS_IS_OK(status)) { - return NT_STATUS_DELETE_PENDING; + return status; } status = pvfs_access_check_simple(pvfs, req, name, diff --git a/source4/ntvfs/posix/pvfs_rename.c b/source4/ntvfs/posix/pvfs_rename.c index 65755e05cb..17c073ccab 100644 --- a/source4/ntvfs/posix/pvfs_rename.c +++ b/source4/ntvfs/posix/pvfs_rename.c @@ -192,8 +192,8 @@ static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs, { struct pvfs_filename *name1, *name2; TALLOC_CTX *mem_ctx = talloc_new(req); + struct odb_lock *lck = NULL; NTSTATUS status; - struct odb_lock *lck, *lck2; /* resolve the wildcard pattern for this name */ fname2 = pvfs_resolve_wildcard(mem_ctx, lp_iconv_convenience(pvfs->ntvfs->ctx->lp_ctx), fname1, fname2); @@ -216,6 +216,7 @@ static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs, status = pvfs_can_rename(pvfs, req, name1, &lck); if (!NT_STATUS_IS_OK(status)) { + talloc_free(lck); goto failed; } @@ -223,7 +224,7 @@ static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs, status = pvfs_resolve_partial(pvfs, mem_ctx, dir_path, fname2, &name2); if (NT_STATUS_IS_OK(status)) { - status = pvfs_can_delete(pvfs, req, name2, &lck2); + status = pvfs_can_delete(pvfs, req, name2, NULL); if (!NT_STATUS_IS_OK(status)) { goto failed; } @@ -311,7 +312,7 @@ static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs, struct pvfs_state *pvfs = ntvfs->private_data; NTSTATUS status; struct pvfs_filename *name1, *name2; - struct odb_lock *lck; + struct odb_lock *lck = NULL; /* resolve the cifs name to a posix name */ status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern1, @@ -354,6 +355,7 @@ static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs, status = pvfs_can_rename(pvfs, req, name1, &lck); if (!NT_STATUS_IS_OK(status)) { + talloc_free(lck); return status; } @@ -375,7 +377,6 @@ static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs, struct pvfs_state *pvfs = ntvfs->private_data; NTSTATUS status; struct pvfs_filename *name1, *name2; - struct odb_lock *lck; switch (ren->ntrename.in.flags) { case RENAME_FLAG_RENAME: @@ -421,7 +422,7 @@ static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs, return status; } - status = pvfs_can_rename(pvfs, req, name1, &lck); + status = pvfs_can_rename(pvfs, req, name1, NULL); if (!NT_STATUS_IS_OK(status)) { return status; } diff --git a/source4/ntvfs/posix/pvfs_setfileinfo.c b/source4/ntvfs/posix/pvfs_setfileinfo.c index 9c78699edb..da1e31e639 100644 --- a/source4/ntvfs/posix/pvfs_setfileinfo.c +++ b/source4/ntvfs/posix/pvfs_setfileinfo.c @@ -142,8 +142,6 @@ static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs, /* if the destination exists, then check the rename is allowed */ if (name2->exists) { - struct odb_lock *lck; - if (strcmp(name2->full_name, name->full_name) == 0) { /* rename to same name is null-op */ return NT_STATUS_OK; @@ -153,7 +151,10 @@ static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs, return NT_STATUS_OBJECT_NAME_COLLISION; } - status = pvfs_can_delete(pvfs, req, name2, &lck); + status = pvfs_can_delete(pvfs, req, name2, NULL); + if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) { + return NT_STATUS_ACCESS_DENIED; + } if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) { return NT_STATUS_ACCESS_DENIED; } @@ -354,6 +355,9 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs, case RAW_SFILEINFO_ALLOCATION_INFO: case RAW_SFILEINFO_ALLOCATION_INFORMATION: + status = pvfs_break_level2_oplocks(f); + NT_STATUS_NOT_OK_RETURN(status); + newstats.dos.alloc_size = info->allocation_info.in.alloc_size; if (newstats.dos.alloc_size < newstats.st.st_size) { newstats.st.st_size = newstats.dos.alloc_size; @@ -364,6 +368,9 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs, case RAW_SFILEINFO_END_OF_FILE_INFO: case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + status = pvfs_break_level2_oplocks(f); + NT_STATUS_NOT_OK_RETURN(status); + newstats.st.st_size = info->end_of_file_info.in.size; break; @@ -465,6 +472,84 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs, return pvfs_dosattrib_save(pvfs, h->name, h->fd); } +/* + retry an open after a sharing violation +*/ +static void pvfs_retry_setpathinfo(struct pvfs_odb_retry *r, + struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, + void *_info, + void *private_data, + enum pvfs_wait_notice reason) +{ + union smb_setfileinfo *info = talloc_get_type(_info, + union smb_setfileinfo); + NTSTATUS status = NT_STATUS_INTERNAL_ERROR; + + talloc_free(r); + + switch (reason) { + case PVFS_WAIT_CANCEL: +/*TODO*/ + status = NT_STATUS_CANCELLED; + break; + case PVFS_WAIT_TIMEOUT: + /* if it timed out, then give the failure + immediately */ +/*TODO*/ + status = NT_STATUS_SHARING_VIOLATION; + break; + case PVFS_WAIT_EVENT: + + /* try the open again, which could trigger another retry setup + if it wants to, so we have to unmark the async flag so we + will know if it does a second async reply */ + req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC; + + status = pvfs_setpathinfo(ntvfs, req, info); + if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) { + /* the 2nd try also replied async, so we don't send + the reply yet */ + return; + } + + /* re-mark it async, just in case someone up the chain does + paranoid checking */ + req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC; + break; + } + + /* send the reply up the chain */ + req->async_states->status = status; + req->async_states->send_fn(req); +} + +/* + setup for a unlink retry after a sharing violation + or a non granted oplock +*/ +static NTSTATUS pvfs_setpathinfo_setup_retry(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, + union smb_setfileinfo *info, + struct odb_lock *lck, + NTSTATUS status) +{ + struct pvfs_state *pvfs = ntvfs->private_data; + struct timeval end_time; + + if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) { + end_time = timeval_add(&req->statistics.request_time, + 0, pvfs->sharing_violation_delay); + } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) { + end_time = timeval_add(&req->statistics.request_time, + pvfs->oplock_break_timeout, 0); + } else { + return NT_STATUS_INTERNAL_ERROR; + } + + return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, info, NULL, + pvfs_retry_setpathinfo); +} /* set info on a pathname @@ -479,6 +564,7 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs, struct utimbuf unix_times; uint32_t access_needed; uint32_t change_mask = 0; + struct odb_lock *lck = NULL; /* resolve the cifs name to a posix name */ status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path, @@ -553,6 +639,20 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs, case RAW_SFILEINFO_ALLOCATION_INFO: case RAW_SFILEINFO_ALLOCATION_INFORMATION: + status = pvfs_can_update_file_size(pvfs, req, name, &lck); + /* + * 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_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status); + } + NT_STATUS_NOT_OK_RETURN(status); + if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) { /* strange. Increasing the allocation size via setpathinfo should be silently ignored */ @@ -568,6 +668,20 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs, case RAW_SFILEINFO_END_OF_FILE_INFO: case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + status = pvfs_can_update_file_size(pvfs, req, name, &lck); + /* + * 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_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status); + } + NT_STATUS_NOT_OK_RETURN(status); + newstats.st.st_size = info->end_of_file_info.in.size; break; diff --git a/source4/ntvfs/posix/pvfs_unlink.c b/source4/ntvfs/posix/pvfs_unlink.c index ef56d99fb5..7a2d964b9d 100644 --- a/source4/ntvfs/posix/pvfs_unlink.c +++ b/source4/ntvfs/posix/pvfs_unlink.c @@ -23,71 +23,94 @@ #include "vfs_posix.h" #include "system/dir.h" - /* - unlink a stream - */ -static NTSTATUS pvfs_unlink_stream(struct pvfs_state *pvfs, - struct ntvfs_request *req, - struct pvfs_filename *name, - uint16_t attrib) + retry an open after a sharing violation +*/ +static void pvfs_retry_unlink(struct pvfs_odb_retry *r, + struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, + void *_io, + void *private_data, + enum pvfs_wait_notice reason) { - NTSTATUS status; - struct odb_lock *lck; + union smb_unlink *io = talloc_get_type(_io, union smb_unlink); + NTSTATUS status = NT_STATUS_INTERNAL_ERROR; + + talloc_free(r); + + switch (reason) { + case PVFS_WAIT_CANCEL: +/*TODO*/ + status = NT_STATUS_CANCELLED; + break; + case PVFS_WAIT_TIMEOUT: + /* if it timed out, then give the failure + immediately */ +/*TODO*/ + status = NT_STATUS_SHARING_VIOLATION; + break; + case PVFS_WAIT_EVENT: + + /* try the open again, which could trigger another retry setup + if it wants to, so we have to unmark the async flag so we + will know if it does a second async reply */ + req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC; + + status = pvfs_unlink(ntvfs, req, io); + if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) { + /* the 2nd try also replied async, so we don't send + the reply yet */ + return; + } - if (!name->stream_exists) { - return NT_STATUS_OBJECT_NAME_NOT_FOUND; + /* re-mark it async, just in case someone up the chain does + paranoid checking */ + req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC; + break; } - /* make sure its matches the given attributes */ - status = pvfs_match_attrib(pvfs, name, attrib, 0); - if (!NT_STATUS_IS_OK(status)) { - return status; - } + /* send the reply up the chain */ + req->async_states->status = status; + req->async_states->send_fn(req); +} - status = pvfs_can_delete(pvfs, req, name, &lck); - if (!NT_STATUS_IS_OK(status)) { - return status; +/* + setup for a unlink retry after a sharing violation + or a non granted oplock +*/ +static NTSTATUS pvfs_unlink_setup_retry(struct ntvfs_module_context *ntvfs, + struct ntvfs_request *req, + union smb_unlink *io, + struct odb_lock *lck, + NTSTATUS status) +{ + struct pvfs_state *pvfs = ntvfs->private_data; + struct timeval end_time; + + if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) { + end_time = timeval_add(&req->statistics.request_time, + 0, pvfs->sharing_violation_delay); + } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) { + end_time = timeval_add(&req->statistics.request_time, + pvfs->oplock_break_timeout, 0); + } else { + return NT_STATUS_INTERNAL_ERROR; } - return pvfs_stream_delete(pvfs, name, -1); + return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL, + pvfs_retry_unlink); } /* - unlink one file + unlink a file */ -static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs, - struct ntvfs_request *req, - const char *unix_path, - const char *fname, uint32_t attrib) +static NTSTATUS pvfs_unlink_file(struct pvfs_state *pvfs, + struct pvfs_filename *name) { - struct pvfs_filename *name; NTSTATUS status; - struct odb_lock *lck; - - /* get a pvfs_filename object */ - status = pvfs_resolve_partial(pvfs, req, - unix_path, fname, &name); - if (!NT_STATUS_IS_OK(status)) { - return status; - } - - /* make sure its matches the given attributes */ - status = pvfs_match_attrib(pvfs, name, attrib, 0); - if (!NT_STATUS_IS_OK(status)) { - talloc_free(name); - return status; - } - - status = pvfs_can_delete(pvfs, req, name, &lck); - if (!NT_STATUS_IS_OK(status)) { - talloc_free(name); - return status; - } if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) { - talloc_free(name); return NT_STATUS_FILE_IS_A_DIRECTORY; } @@ -110,12 +133,57 @@ static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs, name->full_name); } - talloc_free(name); - return status; } /* + unlink one file +*/ +static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs, + struct ntvfs_request *req, + union smb_unlink *unl, + struct pvfs_filename *name) +{ + NTSTATUS status; + struct odb_lock *lck = NULL; + + /* make sure its matches the given attributes */ + status = pvfs_match_attrib(pvfs, name, + unl->unlink.in.attrib, 0); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = pvfs_can_delete(pvfs, req, name, &lck); + + /* + * 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_unlink_setup_retry(pvfs->ntvfs, req, unl, lck, status); + } + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (name->stream_name) { + if (!name->stream_exists) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + return pvfs_stream_delete(pvfs, name, -1); + } + + return pvfs_unlink_file(pvfs, name); +} + +/* delete a file - the dirtype specifies the file types to include in the search. The name can contain CIFS wildcards, but rarely does (except with OS/2 clients) */ @@ -147,8 +215,8 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs, return NT_STATUS_FILE_IS_A_DIRECTORY; } - if (name->stream_name) { - return pvfs_unlink_stream(pvfs, req, name, unl->unlink.in.attrib); + if (!name->has_wildcard) { + return pvfs_unlink_one(pvfs, req, unl, name); } /* get list of matching files */ @@ -158,6 +226,7 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs, } status = NT_STATUS_NO_SUCH_FILE; + talloc_free(name); ofs = 0; @@ -168,10 +237,20 @@ NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs, return NT_STATUS_OBJECT_NAME_INVALID; } - status = pvfs_unlink_one(pvfs, req, pvfs_list_unix_path(dir), fname, unl->unlink.in.attrib); + /* get a pvfs_filename object */ + status = pvfs_resolve_partial(pvfs, req, + pvfs_list_unix_path(dir), + fname, &name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + status = pvfs_unlink_one(pvfs, req, unl, name); if (NT_STATUS_IS_OK(status)) { total_deleted++; } + + talloc_free(name); } if (total_deleted > 0) { diff --git a/source4/ntvfs/posix/pvfs_wait.c b/source4/ntvfs/posix/pvfs_wait.c index 989985a033..291250befd 100644 --- a/source4/ntvfs/posix/pvfs_wait.c +++ b/source4/ntvfs/posix/pvfs_wait.c @@ -31,7 +31,7 @@ struct pvfs_wait { struct pvfs_wait *next, *prev; struct pvfs_state *pvfs; void (*handler)(void *, enum pvfs_wait_notice); - void *private; + void *private_data; int msg_type; struct messaging_context *msg_ctx; struct event_context *ev; @@ -45,20 +45,23 @@ struct pvfs_wait { previous ntvfs handlers in the chain (such as security context) */ NTSTATUS pvfs_async_setup(struct ntvfs_module_context *ntvfs, - struct ntvfs_request *req, void *private) + struct ntvfs_request *req, void *private_data) { - struct pvfs_wait *pwait = private; - pwait->handler(pwait->private, pwait->reason); + struct pvfs_wait *pwait = talloc_get_type(private_data, + struct pvfs_wait); + pwait->handler(pwait->private_data, pwait->reason); return NT_STATUS_OK; } /* receive a completion message for a wait */ -static void pvfs_wait_dispatch(struct messaging_context *msg, void *private, uint32_t msg_type, +static void pvfs_wait_dispatch(struct messaging_context *msg, + void *private_data, uint32_t msg_type, struct server_id src, DATA_BLOB *data) { - struct pvfs_wait *pwait = private; + struct pvfs_wait *pwait = talloc_get_type(private_data, + struct pvfs_wait); struct ntvfs_request *req; void *p = NULL; @@ -70,7 +73,7 @@ static void pvfs_wait_dispatch(struct messaging_context *msg, void *private, uin pp = (void **)data->data; p = *pp; } - if (p == NULL || p != pwait->private) { + if (p == NULL || p != pwait->private_data) { return; } @@ -91,9 +94,11 @@ static void pvfs_wait_dispatch(struct messaging_context *msg, void *private, uin receive a timeout on a message wait */ static void pvfs_wait_timeout(struct event_context *ev, - struct timed_event *te, struct timeval t, void *private) + struct timed_event *te, struct timeval t, + void *private_data) { - struct pvfs_wait *pwait = talloc_get_type(private, struct pvfs_wait); + struct pvfs_wait *pwait = talloc_get_type(private_data, + struct pvfs_wait); struct ntvfs_request *req = pwait->req; pwait->reason = PVFS_WAIT_TIMEOUT; @@ -126,12 +131,12 @@ static int pvfs_wait_destructor(struct pvfs_wait *pwait) if msg_type == -1 then no message is registered, and it is assumed that the caller handles any messaging setup needed */ -void *pvfs_wait_message(struct pvfs_state *pvfs, - struct ntvfs_request *req, - int msg_type, - struct timeval end_time, - void (*fn)(void *, enum pvfs_wait_notice), - void *private) +struct pvfs_wait *pvfs_wait_message(struct pvfs_state *pvfs, + struct ntvfs_request *req, + int msg_type, + struct timeval end_time, + void (*fn)(void *, enum pvfs_wait_notice), + void *private_data) { struct pvfs_wait *pwait; @@ -140,7 +145,7 @@ void *pvfs_wait_message(struct pvfs_state *pvfs, return NULL; } - pwait->private = private; + pwait->private_data = private_data; pwait->handler = fn; pwait->msg_ctx = pvfs->ntvfs->ctx->msg_ctx; pwait->ev = pvfs->ntvfs->ctx->event_ctx; diff --git a/source4/ntvfs/posix/pvfs_write.c b/source4/ntvfs/posix/pvfs_write.c index 5a11f01952..dda8c83407 100644 --- a/source4/ntvfs/posix/pvfs_write.c +++ b/source4/ntvfs/posix/pvfs_write.c @@ -57,7 +57,10 @@ NTSTATUS pvfs_write(struct ntvfs_module_context *ntvfs, wr->writex.in.count, WRITE_LOCK); NT_STATUS_NOT_OK_RETURN(status); - + + status = pvfs_break_level2_oplocks(f); + NT_STATUS_NOT_OK_RETURN(status); + if (f->handle->name->stream_name) { ret = pvfs_stream_write(pvfs, f->handle, diff --git a/source4/ntvfs/posix/vfs_posix.c b/source4/ntvfs/posix/vfs_posix.c index e058e6f546..ca874d1db1 100644 --- a/source4/ntvfs/posix/vfs_posix.c +++ b/source4/ntvfs/posix/vfs_posix.c @@ -91,6 +91,10 @@ static void pvfs_setup_options(struct pvfs_state *pvfs) PVFS_SHARE_DELAY, PVFS_SHARE_DELAY_DEFAULT); + pvfs->oplock_break_timeout = share_int_option(scfg, + PVFS_OPLOCK_TIMEOUT, + PVFS_OPLOCK_TIMEOUT_DEFAULT); + pvfs->share_name = talloc_strdup(pvfs, scfg->name); pvfs->fs_attribs = diff --git a/source4/ntvfs/posix/vfs_posix.h b/source4/ntvfs/posix/vfs_posix.h index a660da329a..4d22a91714 100644 --- a/source4/ntvfs/posix/vfs_posix.h +++ b/source4/ntvfs/posix/vfs_posix.h @@ -28,6 +28,9 @@ #include "ntvfs/common/ntvfs_common.h" #include "dsdb/samdb/samdb.h" +struct pvfs_wait; +struct pvfs_oplock; + /* this is the private structure for the posix vfs backend. It is used to hold per-connection (per tree connect) state information */ struct pvfs_state { @@ -49,9 +52,12 @@ struct pvfs_state { ntcancel */ struct pvfs_wait *wait_list; - /* the sharing violation timeout */ + /* the sharing violation timeout (nsecs) */ uint_t sharing_violation_delay; + /* the oplock break timeout (secs) */ + uint_t oplock_break_timeout; + /* filesystem attributes (see FS_ATTR_*) */ uint32_t fs_attribs; @@ -152,6 +158,13 @@ struct pvfs_file_handle { bool have_opendb_entry; + /* + * we need to wait for oplock break requests from other processes, + * and we need to remember the pvfs_file so we can correctly + * forward the oplock break to the client + */ + struct pvfs_oplock *oplock; + /* we need this hook back to our parent for lock destruction */ struct pvfs_state *pvfs; @@ -228,10 +241,16 @@ struct pvfs_dir; /* types of notification for pvfs wait events */ enum pvfs_wait_notice {PVFS_WAIT_EVENT, PVFS_WAIT_TIMEOUT, PVFS_WAIT_CANCEL}; +/* + state of a pending retry +*/ +struct pvfs_odb_retry; + #define PVFS_EADB "posix:eadb" #define PVFS_XATTR "posix:xattr" #define PVFS_FAKE_OPLOCKS "posix:fakeoplocks" #define PVFS_SHARE_DELAY "posix:sharedelay" +#define PVFS_OPLOCK_TIMEOUT "posix:oplocktimeout" #define PVFS_ALLOCATION_ROUNDING "posix:allocationrounding" #define PVFS_SEARCH_INACTIVITY "posix:searchinactivity" #define PVFS_ACL "posix:acl" @@ -239,7 +258,8 @@ enum pvfs_wait_notice {PVFS_WAIT_EVENT, PVFS_WAIT_TIMEOUT, PVFS_WAIT_CANCEL}; #define PVFS_XATTR_DEFAULT true #define PVFS_FAKE_OPLOCKS_DEFAULT false -#define PVFS_SHARE_DELAY_DEFAULT 1000000 +#define PVFS_SHARE_DELAY_DEFAULT 1000000 /* nsecs */ +#define PVFS_OPLOCK_TIMEOUT_DEFAULT 30 /* secs */ #define PVFS_ALLOCATION_ROUNDING_DEFAULT 512 #define PVFS_SEARCH_INACTIVITY_DEFAULT 300 |