2 Unix SMB/CIFS implementation.
4 POSIX NTVFS backend - open and close
6 Copyright (C) Andrew Tridgell 2004
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "vfs_posix.h"
24 #include "system/dir.h"
25 #include "system/time.h"
26 #include "lib/util/dlinklist.h"
27 #include "messaging/messaging.h"
28 #include "librpc/gen_ndr/xattr.h"
31 find open file handle given fnum
33 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
34 struct ntvfs_request *req, struct ntvfs_handle *h)
39 p = ntvfs_handle_get_backend_data(h, pvfs->ntvfs);
42 f = talloc_get_type(p, struct pvfs_file);
49 cleanup a open directory handle
51 static int pvfs_dir_handle_destructor(struct pvfs_file_handle *h)
53 if (h->have_opendb_entry) {
56 const char *delete_path = NULL;
58 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
60 DEBUG(0,("Unable to lock opendb for close\n"));
64 status = odb_close_file(lck, h, &delete_path);
65 if (!NT_STATUS_IS_OK(status)) {
66 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
67 h->name->full_name, nt_errstr(status)));
70 if (h->name->stream_name == NULL && delete_path) {
71 status = pvfs_xattr_unlink_hook(h->pvfs, delete_path);
72 if (!NT_STATUS_IS_OK(status)) {
73 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
74 delete_path, nt_errstr(status)));
76 if (rmdir(delete_path) != 0) {
77 DEBUG(0,("pvfs_dir_handle_destructor: failed to rmdir '%s' - %s\n",
78 delete_path, strerror(errno)));
89 cleanup a open directory fnum
91 static int pvfs_dir_fnum_destructor(struct pvfs_file *f)
93 DLIST_REMOVE(f->pvfs->files.list, f);
94 ntvfs_handle_remove_backend_data(f->ntvfs, f->pvfs->ntvfs);
100 setup any EAs and the ACL on newly created files/directories
102 static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
103 struct ntvfs_request *req,
104 struct pvfs_filename *name,
105 int fd, struct pvfs_file *f,
110 /* setup any EAs that were asked for */
111 if (io->ntcreatex.in.ea_list) {
112 status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
113 io->ntcreatex.in.ea_list->num_eas,
114 io->ntcreatex.in.ea_list->eas);
115 if (!NT_STATUS_IS_OK(status)) {
120 /* setup an initial sec_desc if requested */
121 if (io->ntcreatex.in.sec_desc) {
122 union smb_setfileinfo set;
124 * TODO: set the full ACL!
125 * - vista denies the creation of the file with NT_STATUS_PRIVILEGE_NOT_HELD,
126 * when a SACL is present on the sd,
127 * but the user doesn't have SeSecurityPrivilege
130 set.set_secdesc.in.file.ntvfs = f->ntvfs;
131 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
132 set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
134 status = pvfs_acl_set(pvfs, req, name, fd, SEC_STD_WRITE_DAC, &set);
136 /* otherwise setup an inherited acl from the parent */
137 status = pvfs_acl_inherit(pvfs, req, name, fd);
144 form the lock context used for opendb locking. Note that we must
145 zero here to take account of possible padding on some architectures
147 NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
148 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
154 ZERO_STRUCT(lock_context);
156 lock_context.device = name->st.st_dev;
157 lock_context.inode = name->st.st_ino;
159 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
160 if (key->data == NULL) {
161 return NT_STATUS_NO_MEMORY;
171 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
172 struct ntvfs_request *req,
173 struct pvfs_filename *name,
177 struct ntvfs_handle *h;
179 uint32_t create_action;
180 uint32_t access_mask = io->generic.in.access_mask;
181 struct odb_lock *lck;
183 uint32_t create_options;
184 uint32_t share_access;
187 create_options = io->generic.in.create_options;
188 share_access = io->generic.in.share_access;
190 forced = (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)?true:false;
192 if (name->stream_name) {
194 return NT_STATUS_NOT_A_DIRECTORY;
196 return NT_STATUS_FILE_IS_A_DIRECTORY;
200 /* if the client says it must be a directory, and it isn't,
202 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
203 return NT_STATUS_NOT_A_DIRECTORY;
206 /* found with gentest */
207 if (io->ntcreatex.in.access_mask == SEC_FLAG_MAXIMUM_ALLOWED &&
208 (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) &&
209 (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
210 return NT_STATUS_INVALID_PARAMETER;
213 switch (io->generic.in.open_disposition) {
214 case NTCREATEX_DISP_OPEN_IF:
217 case NTCREATEX_DISP_OPEN:
219 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
223 case NTCREATEX_DISP_CREATE:
225 return NT_STATUS_OBJECT_NAME_COLLISION;
229 case NTCREATEX_DISP_OVERWRITE_IF:
230 case NTCREATEX_DISP_OVERWRITE:
231 case NTCREATEX_DISP_SUPERSEDE:
233 return NT_STATUS_INVALID_PARAMETER;
236 status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
237 NT_STATUS_NOT_OK_RETURN(status);
239 f = talloc(h, struct pvfs_file);
241 return NT_STATUS_NO_MEMORY;
244 f->handle = talloc(f, struct pvfs_file_handle);
245 if (f->handle == NULL) {
246 return NT_STATUS_NO_MEMORY;
250 /* check the security descriptor */
251 status = pvfs_access_check(pvfs, req, name, &access_mask);
253 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
255 if (!NT_STATUS_IS_OK(status)) {
261 f->pending_list = NULL;
263 f->share_access = io->generic.in.share_access;
264 f->impersonation = io->generic.in.impersonation;
265 f->access_mask = access_mask;
266 f->brl_handle = NULL;
267 f->notify_buffer = NULL;
270 f->handle->pvfs = pvfs;
271 f->handle->name = talloc_steal(f->handle, name);
273 f->handle->odb_locking_key = data_blob(NULL, 0);
274 f->handle->create_options = io->generic.in.create_options;
275 f->handle->seek_offset = 0;
276 f->handle->position = 0;
278 f->handle->oplock = NULL;
279 f->handle->open_completed = false;
281 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
282 pvfs_directory_empty(pvfs, f->handle->name)) {
285 del_on_close = false;
289 /* form the lock context used for opendb locking */
290 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
291 if (!NT_STATUS_IS_OK(status)) {
295 /* get a lock on this file before the actual open */
296 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
298 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
300 /* we were supposed to do a blocking lock, so something
302 return NT_STATUS_INTERNAL_DB_CORRUPTION;
305 /* see if we are allowed to open at the same time as existing opens */
306 status = odb_can_open(lck, name->stream_id,
307 share_access, access_mask, del_on_close,
308 io->generic.in.open_disposition, false);
309 if (!NT_STATUS_IS_OK(status)) {
314 /* now really mark the file as open */
315 status = odb_open_file(lck, f->handle, name->full_name,
316 NULL, false, OPLOCK_NONE, NULL);
318 if (!NT_STATUS_IS_OK(status)) {
323 f->handle->have_opendb_entry = true;
326 DLIST_ADD(pvfs->files.list, f);
328 /* setup destructors to avoid leaks on abnormal termination */
329 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
330 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
333 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
334 mode_t mode = pvfs_fileperms(pvfs, attrib);
336 if (mkdir(name->full_name, mode) == -1) {
337 return pvfs_map_errno(pvfs,errno);
340 pvfs_xattr_unlink_hook(pvfs, name->full_name);
342 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
343 if (!NT_STATUS_IS_OK(status)) {
347 status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, f, io);
348 if (!NT_STATUS_IS_OK(status)) {
352 /* form the lock context used for opendb locking */
353 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
354 if (!NT_STATUS_IS_OK(status)) {
358 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
360 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
362 /* we were supposed to do a blocking lock, so something
364 return NT_STATUS_INTERNAL_DB_CORRUPTION;
367 status = odb_can_open(lck, name->stream_id,
368 share_access, access_mask, del_on_close,
369 io->generic.in.open_disposition, false);
371 if (!NT_STATUS_IS_OK(status)) {
375 status = odb_open_file(lck, f->handle, name->full_name,
376 NULL, false, OPLOCK_NONE, NULL);
378 if (!NT_STATUS_IS_OK(status)) {
382 f->handle->have_opendb_entry = true;
384 create_action = NTCREATEX_ACTION_CREATED;
386 notify_trigger(pvfs->notify_context,
388 FILE_NOTIFY_CHANGE_DIR_NAME,
391 create_action = NTCREATEX_ACTION_EXISTED;
395 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
398 /* the open succeeded, keep this handle permanently */
399 status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
400 if (!NT_STATUS_IS_OK(status)) {
404 f->handle->open_completed = true;
406 io->generic.out.oplock_level = OPLOCK_NONE;
407 io->generic.out.file.ntvfs = h;
408 io->generic.out.create_action = create_action;
409 io->generic.out.create_time = name->dos.create_time;
410 io->generic.out.access_time = name->dos.access_time;
411 io->generic.out.write_time = name->dos.write_time;
412 io->generic.out.change_time = name->dos.change_time;
413 io->generic.out.attrib = name->dos.attrib;
414 io->generic.out.alloc_size = name->dos.alloc_size;
415 io->generic.out.size = name->st.st_size;
416 io->generic.out.file_type = FILE_TYPE_DISK;
417 io->generic.out.ipc_state = 0;
418 io->generic.out.is_directory = 1;
423 rmdir(name->full_name);
428 destroy a struct pvfs_file_handle
430 static int pvfs_handle_destructor(struct pvfs_file_handle *h)
432 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
433 h->name->stream_name) {
435 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
436 if (!NT_STATUS_IS_OK(status)) {
437 DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
438 h->name->stream_name, h->name->full_name));
443 if (close(h->fd) != 0) {
444 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
445 h->fd, h->name->full_name, strerror(errno)));
450 if (h->have_opendb_entry) {
451 struct odb_lock *lck;
453 const char *delete_path = NULL;
455 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
457 DEBUG(0,("Unable to lock opendb for close\n"));
461 status = odb_close_file(lck, h, &delete_path);
462 if (!NT_STATUS_IS_OK(status)) {
463 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
464 h->name->full_name, nt_errstr(status)));
467 if (h->name->stream_name == NULL &&
468 h->open_completed && delete_path) {
469 status = pvfs_xattr_unlink_hook(h->pvfs, delete_path);
470 if (!NT_STATUS_IS_OK(status)) {
471 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
472 delete_path, nt_errstr(status)));
474 if (unlink(delete_path) != 0) {
475 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
476 delete_path, strerror(errno)));
478 notify_trigger(h->pvfs->notify_context,
479 NOTIFY_ACTION_REMOVED,
480 FILE_NOTIFY_CHANGE_FILE_NAME,
493 destroy a struct pvfs_file
495 static int pvfs_fnum_destructor(struct pvfs_file *f)
497 DLIST_REMOVE(f->pvfs->files.list, f);
498 pvfs_lock_close(f->pvfs, f);
499 ntvfs_handle_remove_backend_data(f->ntvfs, f->pvfs->ntvfs);
506 form the lock context used for byte range locking. This is separate
507 from the locking key used for opendb locking as it needs to take
508 account of file streams (each stream is a separate byte range
511 static NTSTATUS pvfs_brl_locking_handle(TALLOC_CTX *mem_ctx,
512 struct pvfs_filename *name,
513 struct ntvfs_handle *ntvfs,
514 struct brl_handle **_h)
516 DATA_BLOB odb_key, key;
518 struct brl_handle *h;
520 status = pvfs_locking_key(name, mem_ctx, &odb_key);
521 NT_STATUS_NOT_OK_RETURN(status);
523 if (name->stream_name == NULL) {
526 key = data_blob_talloc(mem_ctx, NULL,
527 odb_key.length + strlen(name->stream_name) + 1);
528 NT_STATUS_HAVE_NO_MEMORY(key.data);
529 memcpy(key.data, odb_key.data, odb_key.length);
530 memcpy(key.data + odb_key.length,
531 name->stream_name, strlen(name->stream_name) + 1);
532 data_blob_free(&odb_key);
535 h = brl_create_handle(mem_ctx, ntvfs, &key);
536 NT_STATUS_HAVE_NO_MEMORY(h);
545 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
546 struct ntvfs_request *req,
547 struct pvfs_filename *name,
552 struct ntvfs_handle *h;
554 struct odb_lock *lck;
555 uint32_t create_options = io->generic.in.create_options;
556 uint32_t share_access = io->generic.in.share_access;
557 uint32_t access_mask = io->generic.in.access_mask;
561 struct pvfs_filename *parent;
562 uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
563 bool allow_level_II_oplock = false;
565 if (io->ntcreatex.in.file_attr & ~FILE_ATTRIBUTE_ALL_MASK) {
566 return NT_STATUS_INVALID_PARAMETER;
569 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
570 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
571 return NT_STATUS_CANNOT_DELETE;
574 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
575 NT_STATUS_NOT_OK_RETURN(status);
577 /* check that the parent isn't opened with delete on close set */
578 status = pvfs_resolve_parent(pvfs, req, name, &parent);
579 if (NT_STATUS_IS_OK(status)) {
580 DATA_BLOB locking_key;
581 status = pvfs_locking_key(parent, req, &locking_key);
582 NT_STATUS_NOT_OK_RETURN(status);
583 status = odb_get_delete_on_close(pvfs->odb_context, &locking_key,
585 NT_STATUS_NOT_OK_RETURN(status);
587 return NT_STATUS_DELETE_PENDING;
591 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
597 status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
598 NT_STATUS_NOT_OK_RETURN(status);
600 f = talloc(h, struct pvfs_file);
601 NT_STATUS_HAVE_NO_MEMORY(f);
603 f->handle = talloc(f, struct pvfs_file_handle);
604 NT_STATUS_HAVE_NO_MEMORY(f->handle);
606 attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
607 mode = pvfs_fileperms(pvfs, attrib);
609 /* create the file */
610 fd = open(name->full_name, flags | O_CREAT | O_EXCL| O_NONBLOCK, mode);
612 return pvfs_map_errno(pvfs, errno);
615 pvfs_xattr_unlink_hook(pvfs, name->full_name);
617 /* if this was a stream create then create the stream as well */
618 if (name->stream_name) {
619 status = pvfs_stream_create(pvfs, name, fd);
620 if (!NT_STATUS_IS_OK(status)) {
626 /* re-resolve the open fd */
627 status = pvfs_resolve_name_fd(pvfs, fd, name);
628 if (!NT_STATUS_IS_OK(status)) {
633 name->dos.attrib = attrib;
634 status = pvfs_dosattrib_save(pvfs, name, fd);
635 if (!NT_STATUS_IS_OK(status)) {
640 status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, f, io);
641 if (!NT_STATUS_IS_OK(status)) {
645 /* form the lock context used for byte range locking and
647 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
648 if (!NT_STATUS_IS_OK(status)) {
652 status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle);
653 if (!NT_STATUS_IS_OK(status)) {
657 /* grab a lock on the open file record */
658 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
660 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
662 /* we were supposed to do a blocking lock, so something
664 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
668 if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
671 del_on_close = false;
674 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
675 oplock_level = OPLOCK_NONE;
676 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
677 oplock_level = OPLOCK_BATCH;
678 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
679 oplock_level = OPLOCK_EXCLUSIVE;
682 if (req->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS) {
683 allow_level_II_oplock = true;
686 status = odb_can_open(lck, name->stream_id,
687 share_access, access_mask, del_on_close,
688 io->generic.in.open_disposition, false);
689 if (!NT_STATUS_IS_OK(status)) {
691 /* bad news, we must have hit a race - we don't delete the file
692 here as the most likely scenario is that someone else created
693 the file at the same time */
700 f->pending_list = NULL;
702 f->share_access = io->generic.in.share_access;
703 f->access_mask = access_mask;
704 f->impersonation = io->generic.in.impersonation;
705 f->notify_buffer = NULL;
708 f->handle->pvfs = pvfs;
709 f->handle->name = talloc_steal(f->handle, name);
711 f->handle->create_options = io->generic.in.create_options;
712 f->handle->seek_offset = 0;
713 f->handle->position = 0;
715 f->handle->oplock = NULL;
716 f->handle->have_opendb_entry = true;
717 f->handle->open_completed = false;
719 status = odb_open_file(lck, f->handle, name->full_name,
720 &f->handle->fd, allow_level_II_oplock,
721 oplock_level, &oplock_granted);
723 if (!NT_STATUS_IS_OK(status)) {
724 /* bad news, we must have hit a race - we don't delete the file
725 here as the most likely scenario is that someone else created
726 the file at the same time */
731 DLIST_ADD(pvfs->files.list, f);
733 /* setup a destructor to avoid file descriptor leaks on
734 abnormal termination */
735 talloc_set_destructor(f, pvfs_fnum_destructor);
736 talloc_set_destructor(f->handle, pvfs_handle_destructor);
738 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
739 oplock_granted = OPLOCK_BATCH;
740 } else if (oplock_granted != OPLOCK_NONE) {
741 status = pvfs_setup_oplock(f, oplock_granted);
742 if (!NT_STATUS_IS_OK(status)) {
747 io->generic.out.oplock_level = oplock_granted;
748 io->generic.out.file.ntvfs = f->ntvfs;
749 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
750 io->generic.out.create_time = name->dos.create_time;
751 io->generic.out.access_time = name->dos.access_time;
752 io->generic.out.write_time = name->dos.write_time;
753 io->generic.out.change_time = name->dos.change_time;
754 io->generic.out.attrib = name->dos.attrib;
755 io->generic.out.alloc_size = name->dos.alloc_size;
756 io->generic.out.size = name->st.st_size;
757 io->generic.out.file_type = FILE_TYPE_DISK;
758 io->generic.out.ipc_state = 0;
759 io->generic.out.is_directory = 0;
761 /* success - keep the file handle */
762 status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
763 if (!NT_STATUS_IS_OK(status)) {
767 f->handle->open_completed = true;
769 notify_trigger(pvfs->notify_context,
771 FILE_NOTIFY_CHANGE_FILE_NAME,
778 unlink(name->full_name);
783 state of a pending retry
785 struct pvfs_odb_retry {
786 struct ntvfs_module_context *ntvfs;
787 struct ntvfs_request *req;
788 DATA_BLOB odb_locking_key;
791 void (*callback)(struct pvfs_odb_retry *r,
792 struct ntvfs_module_context *ntvfs,
793 struct ntvfs_request *req,
796 enum pvfs_wait_notice reason);
799 /* destroy a pending request */
800 static int pvfs_odb_retry_destructor(struct pvfs_odb_retry *r)
802 struct pvfs_state *pvfs = r->ntvfs->private_data;
803 if (r->odb_locking_key.data) {
804 struct odb_lock *lck;
805 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
807 odb_remove_pending(lck, r);
814 static void pvfs_odb_retry_callback(void *_r, enum pvfs_wait_notice reason)
816 struct pvfs_odb_retry *r = talloc_get_type(_r, struct pvfs_odb_retry);
818 if (reason == PVFS_WAIT_EVENT) {
820 * The pending odb entry is already removed.
821 * We use a null locking key to indicate this
824 data_blob_free(&r->odb_locking_key);
827 r->callback(r, r->ntvfs, r->req, r->io, r->private_data, reason);
831 setup for a retry of a request that was rejected
834 NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
835 struct ntvfs_request *req,
836 struct odb_lock *lck,
837 struct timeval end_time,
840 void (*callback)(struct pvfs_odb_retry *r,
841 struct ntvfs_module_context *ntvfs,
842 struct ntvfs_request *req,
845 enum pvfs_wait_notice reason))
847 struct pvfs_state *pvfs = ntvfs->private_data;
848 struct pvfs_odb_retry *r;
849 struct pvfs_wait *wait_handle;
852 r = talloc(req, struct pvfs_odb_retry);
853 NT_STATUS_HAVE_NO_MEMORY(r);
858 r->private_data = private_data;
859 r->callback = callback;
860 r->odb_locking_key = odb_get_key(r, lck);
861 if (r->odb_locking_key.data == NULL) {
862 return NT_STATUS_NO_MEMORY;
865 /* setup a pending lock */
866 status = odb_open_file_pending(lck, r);
867 if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND,status)) {
869 * maybe only a unix application
872 data_blob_free(&r->odb_locking_key);
873 } else if (!NT_STATUS_IS_OK(status)) {
879 talloc_set_destructor(r, pvfs_odb_retry_destructor);
881 wait_handle = pvfs_wait_message(pvfs, req,
882 MSG_PVFS_RETRY_OPEN, end_time,
883 pvfs_odb_retry_callback, r);
884 if (wait_handle == NULL) {
885 return NT_STATUS_NO_MEMORY;
888 talloc_steal(r, wait_handle);
894 retry an open after a sharing violation
896 static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
897 struct ntvfs_module_context *ntvfs,
898 struct ntvfs_request *req,
901 enum pvfs_wait_notice reason)
903 union smb_open *io = talloc_get_type(_io, union smb_open);
904 struct timeval *final_timeout = NULL;
908 final_timeout = talloc_get_type(private_data,
912 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
913 just a bug in their server, but we better do the same */
914 if (reason == PVFS_WAIT_CANCEL) {
918 if (reason == PVFS_WAIT_TIMEOUT) {
920 !timeval_expired(final_timeout)) {
922 * we need to retry periodictly
923 * after an EAGAIN as there's
924 * no way the kernel tell us
925 * an oplock is released.
929 /* if it timed out, then give the failure
932 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
933 req->async_states->send_fn(req);
940 /* try the open again, which could trigger another retry setup
941 if it wants to, so we have to unmark the async flag so we
942 will know if it does a second async reply */
943 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
945 status = pvfs_open(ntvfs, req, io);
946 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
947 /* the 2nd try also replied async, so we don't send
952 /* re-mark it async, just in case someone up the chain does
954 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
956 /* send the reply up the chain */
957 req->async_states->status = status;
958 req->async_states->send_fn(req);
963 special handling for openx DENY_DOS semantics
965 This function attempts a reference open using an existing handle. If its allowed,
966 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
967 open processing continues.
969 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
970 struct ntvfs_request *req, union smb_open *io,
971 struct pvfs_file *f, struct odb_lock *lck)
973 struct pvfs_state *pvfs = ntvfs->private_data;
974 struct pvfs_file *f2;
975 struct pvfs_filename *name;
978 /* search for an existing open with the right parameters. Note
979 the magic ntcreatex options flag, which is set in the
980 generic mapping code. This might look ugly, but its
981 actually pretty much now w2k does it internally as well.
983 If you look at the BASE-DENYDOS test you will see that a
984 DENY_DOS is a very special case, and in the right
985 circumstances you actually get the _same_ handle back
986 twice, rather than a new handle.
988 for (f2=pvfs->files.list;f2;f2=f2->next) {
990 f2->ntvfs->session_info == req->session_info &&
991 f2->ntvfs->smbpid == req->smbpid &&
992 (f2->handle->create_options &
993 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
994 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
995 (f2->access_mask & SEC_FILE_WRITE_DATA) &&
996 strcasecmp_m(f2->handle->name->original_name,
997 io->generic.in.fname)==0) {
1003 return NT_STATUS_SHARING_VIOLATION;
1006 /* quite an insane set of semantics ... */
1007 if (is_exe_filename(io->generic.in.fname) &&
1008 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
1009 return NT_STATUS_SHARING_VIOLATION;
1013 setup a reference to the existing handle
1015 talloc_free(f->handle);
1016 f->handle = talloc_reference(f, f2->handle);
1020 name = f->handle->name;
1022 io->generic.out.oplock_level = OPLOCK_NONE;
1023 io->generic.out.file.ntvfs = f->ntvfs;
1024 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
1025 io->generic.out.create_time = name->dos.create_time;
1026 io->generic.out.access_time = name->dos.access_time;
1027 io->generic.out.write_time = name->dos.write_time;
1028 io->generic.out.change_time = name->dos.change_time;
1029 io->generic.out.attrib = name->dos.attrib;
1030 io->generic.out.alloc_size = name->dos.alloc_size;
1031 io->generic.out.size = name->st.st_size;
1032 io->generic.out.file_type = FILE_TYPE_DISK;
1033 io->generic.out.ipc_state = 0;
1034 io->generic.out.is_directory = 0;
1036 status = ntvfs_handle_set_backend_data(f->ntvfs, ntvfs, f);
1037 NT_STATUS_NOT_OK_RETURN(status);
1039 return NT_STATUS_OK;
1045 setup for a open retry after a sharing violation
1047 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
1048 struct ntvfs_request *req,
1050 struct pvfs_file *f,
1051 struct odb_lock *lck,
1052 NTSTATUS parent_status)
1054 struct pvfs_state *pvfs = ntvfs->private_data;
1056 struct timeval end_time;
1057 struct timeval *final_timeout = NULL;
1059 if (io->generic.in.create_options &
1060 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
1061 /* see if we can satisfy the request using the special DENY_DOS
1063 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
1064 if (NT_STATUS_IS_OK(status)) {
1069 /* the retry should allocate a new file handle */
1072 if (NT_STATUS_EQUAL(parent_status, NT_STATUS_SHARING_VIOLATION)) {
1073 end_time = timeval_add(&req->statistics.request_time,
1074 0, pvfs->sharing_violation_delay);
1075 } else if (NT_STATUS_EQUAL(parent_status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
1076 end_time = timeval_add(&req->statistics.request_time,
1077 pvfs->oplock_break_timeout, 0);
1078 } else if (NT_STATUS_EQUAL(parent_status, STATUS_MORE_ENTRIES)) {
1080 * we got EAGAIN which means a unix application
1081 * has an oplock or share mode
1083 * we retry every 4/5 of the sharing violation delay
1084 * to see if the unix application
1085 * has released the oplock or share mode.
1087 final_timeout = talloc(req, struct timeval);
1088 NT_STATUS_HAVE_NO_MEMORY(final_timeout);
1089 *final_timeout = timeval_add(&req->statistics.request_time,
1090 pvfs->oplock_break_timeout,
1092 end_time = timeval_current_ofs(0, (pvfs->sharing_violation_delay*4)/5);
1093 end_time = timeval_min(final_timeout, &end_time);
1095 return NT_STATUS_INTERNAL_ERROR;
1098 return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io,
1099 final_timeout, pvfs_retry_open_sharing);
1105 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
1106 struct ntvfs_request *req, union smb_open *io)
1108 struct pvfs_state *pvfs = ntvfs->private_data;
1110 struct pvfs_filename *name;
1111 struct pvfs_file *f;
1112 struct ntvfs_handle *h;
1115 struct odb_lock *lck;
1116 uint32_t create_options;
1117 uint32_t share_access;
1118 uint32_t access_mask;
1120 bool stream_existed, stream_truncate=false;
1121 uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
1122 bool allow_level_II_oplock = false;
1124 /* use the generic mapping code to avoid implementing all the
1125 different open calls. */
1126 if (io->generic.level != RAW_OPEN_GENERIC &&
1127 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
1128 return ntvfs_map_open(ntvfs, req, io);
1131 create_options = io->generic.in.create_options;
1132 share_access = io->generic.in.share_access;
1133 access_mask = io->generic.in.access_mask;
1135 if (share_access & ~NTCREATEX_SHARE_ACCESS_MASK) {
1136 return NT_STATUS_INVALID_PARAMETER;
1139 /* some create options are not supported */
1140 if (create_options & NTCREATEX_OPTIONS_NOT_SUPPORTED_MASK) {
1141 return NT_STATUS_NOT_SUPPORTED;
1144 /* other create options are not allowed */
1145 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
1146 !(access_mask & SEC_STD_DELETE)) {
1147 return NT_STATUS_INVALID_PARAMETER;
1150 if (access_mask & (SEC_MASK_INVALID | SEC_STD_SYNCHRONIZE)) {
1151 return NT_STATUS_ACCESS_DENIED;
1154 if (io->ntcreatex.in.file_attr & (FILE_ATTRIBUTE_DEVICE|
1155 FILE_ATTRIBUTE_VOLUME)) {
1156 return NT_STATUS_INVALID_PARAMETER;
1159 /* resolve the cifs name to a posix name */
1160 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
1161 PVFS_RESOLVE_STREAMS, &name);
1162 if (!NT_STATUS_IS_OK(status)) {
1166 /* if the client specified that it must not be a directory then
1167 check that it isn't */
1168 if (name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
1169 (io->generic.in.create_options & NTCREATEX_OPTIONS_NON_DIRECTORY_FILE)) {
1170 return NT_STATUS_FILE_IS_A_DIRECTORY;
1173 /* if the client specified that it must be a directory then
1175 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
1176 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
1177 return NT_STATUS_NOT_A_DIRECTORY;
1180 /* directory opens are handled separately */
1181 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
1182 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
1183 return pvfs_open_directory(pvfs, req, name, io);
1186 /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
1187 open doesn't match */
1188 io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
1192 switch (io->generic.in.open_disposition) {
1193 case NTCREATEX_DISP_SUPERSEDE:
1194 case NTCREATEX_DISP_OVERWRITE_IF:
1195 if (name->stream_name == NULL) {
1198 stream_truncate = true;
1202 case NTCREATEX_DISP_OPEN:
1203 if (!name->stream_exists) {
1204 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1209 case NTCREATEX_DISP_OVERWRITE:
1210 if (!name->stream_exists) {
1211 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1213 if (name->stream_name == NULL) {
1216 stream_truncate = true;
1220 case NTCREATEX_DISP_CREATE:
1221 if (name->stream_exists) {
1222 return NT_STATUS_OBJECT_NAME_COLLISION;
1227 case NTCREATEX_DISP_OPEN_IF:
1232 return NT_STATUS_INVALID_PARAMETER;
1235 /* handle creating a new file separately */
1236 if (!name->exists) {
1237 status = pvfs_create_file(pvfs, req, name, io);
1238 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1242 /* we've hit a race - the file was created during this call */
1243 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
1247 /* try re-resolving the name */
1248 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
1249 if (!NT_STATUS_IS_OK(status)) {
1252 /* fall through to a normal open */
1255 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1256 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1257 return NT_STATUS_CANNOT_DELETE;
1260 /* check the security descriptor */
1261 status = pvfs_access_check(pvfs, req, name, &access_mask);
1262 if (!NT_STATUS_IS_OK(status)) {
1266 status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
1267 NT_STATUS_NOT_OK_RETURN(status);
1269 f = talloc(h, struct pvfs_file);
1271 return NT_STATUS_NO_MEMORY;
1274 f->handle = talloc(f, struct pvfs_file_handle);
1275 if (f->handle == NULL) {
1276 return NT_STATUS_NO_MEMORY;
1281 f->pending_list = NULL;
1283 f->share_access = io->generic.in.share_access;
1284 f->access_mask = access_mask;
1285 f->impersonation = io->generic.in.impersonation;
1286 f->notify_buffer = NULL;
1289 f->handle->pvfs = pvfs;
1291 f->handle->name = talloc_steal(f->handle, name);
1292 f->handle->create_options = io->generic.in.create_options;
1293 f->handle->seek_offset = 0;
1294 f->handle->position = 0;
1295 f->handle->mode = 0;
1296 f->handle->oplock = NULL;
1297 f->handle->have_opendb_entry = false;
1298 f->handle->open_completed = false;
1300 /* form the lock context used for byte range locking and
1302 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
1303 if (!NT_STATUS_IS_OK(status)) {
1307 status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle);
1308 if (!NT_STATUS_IS_OK(status)) {
1312 /* get a lock on this file before the actual open */
1313 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1315 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1317 /* we were supposed to do a blocking lock, so something
1319 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1322 DLIST_ADD(pvfs->files.list, f);
1324 /* setup a destructor to avoid file descriptor leaks on
1325 abnormal termination */
1326 talloc_set_destructor(f, pvfs_fnum_destructor);
1327 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1330 * Only SMB2 takes care of the delete_on_close,
1333 if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE &&
1334 req->ctx->protocol == PROTOCOL_SMB2) {
1335 del_on_close = true;
1337 del_on_close = false;
1340 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1341 oplock_level = OPLOCK_NONE;
1342 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
1343 oplock_level = OPLOCK_BATCH;
1344 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
1345 oplock_level = OPLOCK_EXCLUSIVE;
1348 if (req->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS) {
1349 allow_level_II_oplock = true;
1352 /* see if we are allowed to open at the same time as existing opens */
1353 status = odb_can_open(lck, name->stream_id,
1354 share_access, access_mask, del_on_close,
1355 io->generic.in.open_disposition, false);
1358 * on a sharing violation we need to retry when the file is closed by
1359 * the other user, or after 1 second
1360 * on a non granted oplock we need to retry when the file is closed by
1361 * the other user, or after 30 seconds
1363 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
1364 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
1365 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1366 return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
1369 if (!NT_STATUS_IS_OK(status)) {
1374 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1380 /* do the actual open */
1381 fd = open(f->handle->name->full_name, flags | O_NONBLOCK);
1383 status = pvfs_map_errno(f->pvfs, errno);
1386 * STATUS_MORE_ENTRIES is EAGAIN or EWOULDBLOCK
1388 if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) &&
1389 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1390 return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
1399 /* now really mark the file as open */
1400 status = odb_open_file(lck, f->handle, name->full_name,
1401 &f->handle->fd, allow_level_II_oplock,
1402 oplock_level, &oplock_granted);
1404 if (!NT_STATUS_IS_OK(status)) {
1409 f->handle->have_opendb_entry = true;
1411 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1412 oplock_granted = OPLOCK_BATCH;
1413 } else if (oplock_granted != OPLOCK_NONE) {
1414 status = pvfs_setup_oplock(f, oplock_granted);
1415 if (!NT_STATUS_IS_OK(status)) {
1421 stream_existed = name->stream_exists;
1423 /* if this was a stream create then create the stream as well */
1424 if (!name->stream_exists) {
1425 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1426 if (!NT_STATUS_IS_OK(status)) {
1430 if (stream_truncate) {
1431 status = pvfs_stream_truncate(pvfs, f->handle->name, fd, 0);
1432 if (!NT_STATUS_IS_OK(status)) {
1439 /* re-resolve the open fd */
1440 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1441 if (!NT_STATUS_IS_OK(status)) {
1446 if (f->handle->name->stream_id == 0 &&
1447 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1448 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1449 /* for overwrite we need to replace file permissions */
1450 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1451 mode_t mode = pvfs_fileperms(pvfs, attrib);
1452 if (fchmod(fd, mode) == -1) {
1454 return pvfs_map_errno(pvfs, errno);
1456 name->dos.attrib = attrib;
1457 status = pvfs_dosattrib_save(pvfs, name, fd);
1458 if (!NT_STATUS_IS_OK(status)) {
1466 status = ntvfs_handle_set_backend_data(h, ntvfs, f);
1467 NT_STATUS_NOT_OK_RETURN(status);
1469 /* mark the open as having completed fully, so delete on close
1471 f->handle->open_completed = true;
1473 io->generic.out.oplock_level = oplock_granted;
1474 io->generic.out.file.ntvfs = h;
1475 io->generic.out.create_action = stream_existed?
1476 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1477 io->generic.out.create_time = name->dos.create_time;
1478 io->generic.out.access_time = name->dos.access_time;
1479 io->generic.out.write_time = name->dos.write_time;
1480 io->generic.out.change_time = name->dos.change_time;
1481 io->generic.out.attrib = name->dos.attrib;
1482 io->generic.out.alloc_size = name->dos.alloc_size;
1483 io->generic.out.size = name->st.st_size;
1484 io->generic.out.file_type = FILE_TYPE_DISK;
1485 io->generic.out.ipc_state = 0;
1486 io->generic.out.is_directory = 0;
1488 return NT_STATUS_OK;
1495 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1496 struct ntvfs_request *req, union smb_close *io)
1498 struct pvfs_state *pvfs = ntvfs->private_data;
1499 struct pvfs_file *f;
1500 struct utimbuf unix_times;
1502 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1503 return NT_STATUS_DOS(ERRSRV, ERRerror);
1506 if (io->generic.level != RAW_CLOSE_CLOSE) {
1507 return ntvfs_map_close(ntvfs, req, io);
1510 f = pvfs_find_fd(pvfs, req, io->close.in.file.ntvfs);
1512 return NT_STATUS_INVALID_HANDLE;
1515 if (!null_time(io->close.in.write_time)) {
1516 unix_times.actime = 0;
1517 unix_times.modtime = io->close.in.write_time;
1518 utime(f->handle->name->full_name, &unix_times);
1523 return NT_STATUS_OK;
1528 logoff - close all file descriptors open by a vuid
1530 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1531 struct ntvfs_request *req)
1533 struct pvfs_state *pvfs = ntvfs->private_data;
1534 struct pvfs_file *f, *next;
1536 for (f=pvfs->files.list;f;f=next) {
1538 if (f->ntvfs->session_info == req->session_info) {
1543 return NT_STATUS_OK;
1548 exit - close files for the current pid
1550 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1551 struct ntvfs_request *req)
1553 struct pvfs_state *pvfs = ntvfs->private_data;
1554 struct pvfs_file *f, *next;
1556 for (f=pvfs->files.list;f;f=next) {
1558 if (f->ntvfs->session_info == req->session_info &&
1559 f->ntvfs->smbpid == req->smbpid) {
1564 return NT_STATUS_OK;
1569 change the delete on close flag on an already open file
1571 NTSTATUS pvfs_set_delete_on_close(struct pvfs_state *pvfs,
1572 struct ntvfs_request *req,
1573 struct pvfs_file *f, bool del_on_close)
1575 struct odb_lock *lck;
1578 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) && del_on_close) {
1579 return NT_STATUS_CANNOT_DELETE;
1582 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
1583 !pvfs_directory_empty(pvfs, f->handle->name)) {
1584 return NT_STATUS_DIRECTORY_NOT_EMPTY;
1588 f->handle->create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1590 f->handle->create_options &= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1593 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1595 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1598 status = odb_set_delete_on_close(lck, del_on_close);
1607 determine if a file can be deleted, or if it is prevented by an
1610 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
1611 struct ntvfs_request *req,
1612 struct pvfs_filename *name,
1613 struct odb_lock **lckp)
1617 struct odb_lock *lck;
1618 uint32_t share_access;
1619 uint32_t access_mask;
1620 bool delete_on_close;
1622 status = pvfs_locking_key(name, name, &key);
1623 if (!NT_STATUS_IS_OK(status)) {
1624 return NT_STATUS_NO_MEMORY;
1627 lck = odb_lock(req, pvfs->odb_context, &key);
1629 DEBUG(0,("Unable to lock opendb for can_delete\n"));
1630 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1633 share_access = NTCREATEX_SHARE_ACCESS_READ |
1634 NTCREATEX_SHARE_ACCESS_WRITE |
1635 NTCREATEX_SHARE_ACCESS_DELETE;
1636 access_mask = SEC_STD_DELETE;
1637 delete_on_close = true;
1639 status = odb_can_open(lck, name->stream_id,
1640 share_access, access_mask, delete_on_close,
1641 NTCREATEX_DISP_OPEN, false);
1643 if (NT_STATUS_IS_OK(status)) {
1644 status = pvfs_access_check_simple(pvfs, req, name, access_mask);
1648 * if it's a sharing violation or we got no oplock
1649 * only keep the lock if the caller requested access
1652 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
1653 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
1659 } else if (!NT_STATUS_IS_OK(status)) {
1672 determine if a file can be renamed, or if it is prevented by an
1675 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
1676 struct ntvfs_request *req,
1677 struct pvfs_filename *name,
1678 struct odb_lock **lckp)
1682 struct odb_lock *lck;
1683 uint32_t share_access;
1684 uint32_t access_mask;
1685 bool delete_on_close;
1687 status = pvfs_locking_key(name, name, &key);
1688 if (!NT_STATUS_IS_OK(status)) {
1689 return NT_STATUS_NO_MEMORY;
1692 lck = odb_lock(req, pvfs->odb_context, &key);
1694 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1695 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1698 share_access = NTCREATEX_SHARE_ACCESS_READ |
1699 NTCREATEX_SHARE_ACCESS_WRITE;
1700 access_mask = SEC_STD_DELETE;
1701 delete_on_close = false;
1703 status = odb_can_open(lck, name->stream_id,
1704 share_access, access_mask, delete_on_close,
1705 NTCREATEX_DISP_OPEN, false);
1708 * if it's a sharing violation or we got no oplock
1709 * only keep the lock if the caller requested access
1712 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
1713 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
1719 } else if (!NT_STATUS_IS_OK(status)) {
1732 determine if the file size of a file can be changed,
1733 or if it is prevented by an already open file
1735 NTSTATUS pvfs_can_update_file_size(struct pvfs_state *pvfs,
1736 struct ntvfs_request *req,
1737 struct pvfs_filename *name,
1738 struct odb_lock **lckp)
1742 struct odb_lock *lck;
1743 uint32_t share_access;
1744 uint32_t access_mask;
1746 bool delete_on_close;
1748 status = pvfs_locking_key(name, name, &key);
1749 if (!NT_STATUS_IS_OK(status)) {
1750 return NT_STATUS_NO_MEMORY;
1753 lck = odb_lock(req, pvfs->odb_context, &key);
1755 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1756 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1759 share_access = NTCREATEX_SHARE_ACCESS_READ |
1760 NTCREATEX_SHARE_ACCESS_WRITE |
1761 NTCREATEX_SHARE_ACCESS_DELETE;
1763 * I would have thought that we would need to pass
1764 * SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA here too
1766 * But you only need SEC_FILE_WRITE_ATTRIBUTE permissions
1767 * to set the filesize.
1771 access_mask = SEC_FILE_WRITE_ATTRIBUTE;
1772 delete_on_close = false;
1773 break_to_none = true;
1775 status = odb_can_open(lck, name->stream_id,
1776 share_access, access_mask, delete_on_close,
1777 NTCREATEX_DISP_OPEN, break_to_none);
1780 * if it's a sharing violation or we got no oplock
1781 * only keep the lock if the caller requested access
1784 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
1785 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
1791 } else if (!NT_STATUS_IS_OK(status)) {
1804 determine if file meta data can be accessed, or if it is prevented by an
1807 NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
1808 struct ntvfs_request *req,
1809 struct pvfs_filename *name)
1813 struct odb_lock *lck;
1814 uint32_t share_access;
1815 uint32_t access_mask;
1816 bool delete_on_close;
1818 status = pvfs_locking_key(name, name, &key);
1819 if (!NT_STATUS_IS_OK(status)) {
1820 return NT_STATUS_NO_MEMORY;
1823 lck = odb_lock(req, pvfs->odb_context, &key);
1825 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1826 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1829 share_access = NTCREATEX_SHARE_ACCESS_READ |
1830 NTCREATEX_SHARE_ACCESS_WRITE;
1831 access_mask = SEC_FILE_READ_ATTRIBUTE;
1832 delete_on_close = false;
1834 status = odb_can_open(lck, name->stream_id,
1835 share_access, access_mask, delete_on_close,
1836 NTCREATEX_DISP_OPEN, false);
1838 if (!NT_STATUS_IS_OK(status)) {
1847 determine if delete on close is set on
1849 bool pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h)
1854 status = odb_get_delete_on_close(pvfs->odb_context, &h->odb_locking_key,
1856 if (!NT_STATUS_IS_OK(status)) {
1857 DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
1861 return del_on_close;