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)
56 if (h->name->stream_name == NULL &&
57 pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
60 status = pvfs_xattr_unlink_hook(h->pvfs, path);
61 if (!NT_STATUS_IS_OK(status)) {
62 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
63 path, nt_errstr(status)));
65 if (rmdir(path) != 0) {
66 DEBUG(0,("pvfs_dir_handle_destructor: failed to rmdir '%s' - %s\n",
67 path, strerror(errno)));
73 if (h->have_opendb_entry) {
77 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
79 DEBUG(0,("Unable to lock opendb for close\n"));
83 status = odb_close_file(lck, h);
84 if (!NT_STATUS_IS_OK(status)) {
85 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
86 h->name->full_name, nt_errstr(status)));
96 cleanup a open directory fnum
98 static int pvfs_dir_fnum_destructor(struct pvfs_file *f)
100 DLIST_REMOVE(f->pvfs->files.list, f);
101 ntvfs_handle_remove_backend_data(f->ntvfs, f->pvfs->ntvfs);
107 setup any EAs and the ACL on newly created files/directories
109 static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
110 struct ntvfs_request *req,
111 struct pvfs_filename *name,
112 int fd, struct pvfs_file *f,
117 /* setup any EAs that were asked for */
118 if (io->ntcreatex.in.ea_list) {
119 status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
120 io->ntcreatex.in.ea_list->num_eas,
121 io->ntcreatex.in.ea_list->eas);
122 if (!NT_STATUS_IS_OK(status)) {
127 /* setup an initial sec_desc if requested */
128 if (io->ntcreatex.in.sec_desc) {
129 union smb_setfileinfo set;
131 * TODO: set the full ACL!
132 * - vista denies the creation of the file with NT_STATUS_PRIVILEGE_NOT_HELD,
133 * when a SACL is present on the sd,
134 * but the user doesn't have SeSecurityPrivilege
137 set.set_secdesc.in.file.ntvfs = f->ntvfs;
138 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
139 set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
141 status = pvfs_acl_set(pvfs, req, name, fd, SEC_STD_WRITE_DAC, &set);
143 /* otherwise setup an inherited acl from the parent */
144 status = pvfs_acl_inherit(pvfs, req, name, fd);
151 form the lock context used for opendb locking. Note that we must
152 zero here to take account of possible padding on some architectures
154 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
155 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
161 ZERO_STRUCT(lock_context);
163 lock_context.device = name->st.st_dev;
164 lock_context.inode = name->st.st_ino;
166 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
167 if (key->data == NULL) {
168 return NT_STATUS_NO_MEMORY;
178 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
179 struct ntvfs_request *req,
180 struct pvfs_filename *name,
184 struct ntvfs_handle *h;
186 uint32_t create_action;
187 uint32_t access_mask = io->generic.in.access_mask;
188 struct odb_lock *lck;
190 uint32_t create_options;
191 uint32_t share_access;
193 create_options = io->generic.in.create_options;
194 share_access = io->generic.in.share_access;
196 if (name->stream_name) {
197 return NT_STATUS_NOT_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 switch (io->generic.in.open_disposition) {
207 case NTCREATEX_DISP_OPEN_IF:
210 case NTCREATEX_DISP_OPEN:
212 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
216 case NTCREATEX_DISP_CREATE:
218 return NT_STATUS_OBJECT_NAME_COLLISION;
222 case NTCREATEX_DISP_OVERWRITE_IF:
223 case NTCREATEX_DISP_OVERWRITE:
224 case NTCREATEX_DISP_SUPERSEDE:
226 return NT_STATUS_INVALID_PARAMETER;
229 status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
230 NT_STATUS_NOT_OK_RETURN(status);
232 f = talloc(h, struct pvfs_file);
234 return NT_STATUS_NO_MEMORY;
237 f->handle = talloc(f, struct pvfs_file_handle);
238 if (f->handle == NULL) {
239 return NT_STATUS_NO_MEMORY;
243 /* check the security descriptor */
244 status = pvfs_access_check(pvfs, req, name, &access_mask);
246 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
248 if (!NT_STATUS_IS_OK(status)) {
254 f->pending_list = NULL;
256 f->share_access = io->generic.in.share_access;
257 f->impersonation = io->generic.in.impersonation;
258 f->access_mask = access_mask;
259 f->brl_handle = NULL;
260 f->notify_buffer = NULL;
263 f->handle->pvfs = pvfs;
264 f->handle->name = talloc_steal(f->handle, name);
266 f->handle->odb_locking_key = data_blob(NULL, 0);
267 f->handle->create_options = io->generic.in.create_options;
268 f->handle->seek_offset = 0;
269 f->handle->position = 0;
271 f->handle->oplock = NULL;
272 f->handle->sticky_write_time = false;
273 f->handle->open_completed = false;
275 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
276 pvfs_directory_empty(pvfs, f->handle->name)) {
279 del_on_close = false;
283 /* form the lock context used for opendb locking */
284 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
285 if (!NT_STATUS_IS_OK(status)) {
289 /* get a lock on this file before the actual open */
290 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
292 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
294 /* we were supposed to do a blocking lock, so something
296 return NT_STATUS_INTERNAL_DB_CORRUPTION;
299 /* see if we are allowed to open at the same time as existing opens */
300 status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
301 share_access, access_mask, del_on_close,
302 io->generic.in.open_disposition,
303 false, OPLOCK_NONE, NULL);
305 if (!NT_STATUS_IS_OK(status)) {
310 f->handle->have_opendb_entry = true;
313 DLIST_ADD(pvfs->files.list, f);
315 /* setup destructors to avoid leaks on abnormal termination */
316 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
317 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
320 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
321 mode_t mode = pvfs_fileperms(pvfs, attrib);
323 if (mkdir(name->full_name, mode) == -1) {
324 return pvfs_map_errno(pvfs,errno);
327 pvfs_xattr_unlink_hook(pvfs, name->full_name);
329 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
330 if (!NT_STATUS_IS_OK(status)) {
334 status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, f, io);
335 if (!NT_STATUS_IS_OK(status)) {
339 /* form the lock context used for opendb locking */
340 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
341 if (!NT_STATUS_IS_OK(status)) {
345 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
347 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
349 /* we were supposed to do a blocking lock, so something
351 return NT_STATUS_INTERNAL_DB_CORRUPTION;
354 status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
355 share_access, access_mask, del_on_close,
356 io->generic.in.open_disposition,
357 false, OPLOCK_NONE, NULL);
359 if (!NT_STATUS_IS_OK(status)) {
363 f->handle->have_opendb_entry = true;
365 create_action = NTCREATEX_ACTION_CREATED;
367 notify_trigger(pvfs->notify_context,
369 FILE_NOTIFY_CHANGE_DIR_NAME,
372 create_action = NTCREATEX_ACTION_EXISTED;
376 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
379 /* the open succeeded, keep this handle permanently */
380 status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
381 if (!NT_STATUS_IS_OK(status)) {
385 f->handle->open_completed = true;
387 io->generic.out.oplock_level = OPLOCK_NONE;
388 io->generic.out.file.ntvfs = h;
389 io->generic.out.create_action = create_action;
390 io->generic.out.create_time = name->dos.create_time;
391 io->generic.out.access_time = name->dos.access_time;
392 io->generic.out.write_time = name->dos.write_time;
393 io->generic.out.change_time = name->dos.change_time;
394 io->generic.out.attrib = name->dos.attrib;
395 io->generic.out.alloc_size = name->dos.alloc_size;
396 io->generic.out.size = name->st.st_size;
397 io->generic.out.file_type = FILE_TYPE_DISK;
398 io->generic.out.ipc_state = 0;
399 io->generic.out.is_directory = 1;
404 rmdir(name->full_name);
409 destroy a struct pvfs_file_handle
411 static int pvfs_handle_destructor(struct pvfs_file_handle *h)
416 /* the write time is no longer sticky */
417 if (h->sticky_write_time) {
419 status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
420 if (NT_STATUS_IS_OK(status)) {
421 h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
422 pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
426 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
427 h->name->stream_name) {
429 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
430 if (!NT_STATUS_IS_OK(status)) {
431 DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
432 h->name->stream_name, h->name->full_name));
437 if (close(h->fd) != 0) {
438 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
439 h->fd, h->name->full_name, strerror(errno)));
444 if (h->name->stream_name == NULL &&
446 pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
449 status = pvfs_xattr_unlink_hook(h->pvfs, path);
450 if (!NT_STATUS_IS_OK(status)) {
451 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
452 path, nt_errstr(status)));
454 if (unlink(path) != 0) {
455 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
456 path, strerror(errno)));
458 notify_trigger(h->pvfs->notify_context,
459 NOTIFY_ACTION_REMOVED,
460 FILE_NOTIFY_CHANGE_FILE_NAME,
467 if (h->have_opendb_entry) {
468 struct odb_lock *lck;
471 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
473 DEBUG(0,("Unable to lock opendb for close\n"));
477 status = odb_close_file(lck, h);
478 if (!NT_STATUS_IS_OK(status)) {
479 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
480 h->name->full_name, nt_errstr(status)));
491 destroy a struct pvfs_file
493 static int pvfs_fnum_destructor(struct pvfs_file *f)
495 DLIST_REMOVE(f->pvfs->files.list, f);
496 pvfs_lock_close(f->pvfs, f);
497 ntvfs_handle_remove_backend_data(f->ntvfs, f->pvfs->ntvfs);
504 form the lock context used for byte range locking. This is separate
505 from the locking key used for opendb locking as it needs to take
506 account of file streams (each stream is a separate byte range
509 static NTSTATUS pvfs_brl_locking_handle(TALLOC_CTX *mem_ctx,
510 struct pvfs_filename *name,
511 struct ntvfs_handle *ntvfs,
512 struct brl_handle **_h)
514 DATA_BLOB odb_key, key;
516 struct brl_handle *h;
518 status = pvfs_locking_key(name, mem_ctx, &odb_key);
519 NT_STATUS_NOT_OK_RETURN(status);
521 if (name->stream_name == NULL) {
524 key = data_blob_talloc(mem_ctx, NULL,
525 odb_key.length + strlen(name->stream_name) + 1);
526 NT_STATUS_HAVE_NO_MEMORY(key.data);
527 memcpy(key.data, odb_key.data, odb_key.length);
528 memcpy(key.data + odb_key.length,
529 name->stream_name, strlen(name->stream_name) + 1);
530 data_blob_free(&odb_key);
533 h = brl_create_handle(mem_ctx, ntvfs, &key);
534 NT_STATUS_HAVE_NO_MEMORY(h);
543 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
544 struct ntvfs_request *req,
545 struct pvfs_filename *name,
550 struct ntvfs_handle *h;
552 struct odb_lock *lck;
553 uint32_t create_options = io->generic.in.create_options;
554 uint32_t share_access = io->generic.in.share_access;
555 uint32_t access_mask = io->generic.in.access_mask;
559 struct pvfs_filename *parent;
560 uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
562 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
563 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
564 return NT_STATUS_CANNOT_DELETE;
567 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
568 NT_STATUS_NOT_OK_RETURN(status);
570 /* check that the parent isn't opened with delete on close set */
571 status = pvfs_resolve_parent(pvfs, req, name, &parent);
572 if (NT_STATUS_IS_OK(status)) {
573 DATA_BLOB locking_key;
574 status = pvfs_locking_key(parent, req, &locking_key);
575 NT_STATUS_NOT_OK_RETURN(status);
576 status = odb_get_delete_on_close(pvfs->odb_context, &locking_key,
577 &del_on_close, NULL, NULL);
578 NT_STATUS_NOT_OK_RETURN(status);
580 return NT_STATUS_DELETE_PENDING;
584 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
590 status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
591 NT_STATUS_NOT_OK_RETURN(status);
593 f = talloc(h, struct pvfs_file);
594 NT_STATUS_HAVE_NO_MEMORY(f);
596 f->handle = talloc(f, struct pvfs_file_handle);
597 NT_STATUS_HAVE_NO_MEMORY(f->handle);
599 attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
600 mode = pvfs_fileperms(pvfs, attrib);
602 /* create the file */
603 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
605 return pvfs_map_errno(pvfs, errno);
608 pvfs_xattr_unlink_hook(pvfs, name->full_name);
610 /* if this was a stream create then create the stream as well */
611 if (name->stream_name) {
612 status = pvfs_stream_create(pvfs, name, fd);
613 if (!NT_STATUS_IS_OK(status)) {
619 /* re-resolve the open fd */
620 status = pvfs_resolve_name_fd(pvfs, fd, name);
621 if (!NT_STATUS_IS_OK(status)) {
626 name->dos.attrib = attrib;
627 status = pvfs_dosattrib_save(pvfs, name, fd);
628 if (!NT_STATUS_IS_OK(status)) {
633 status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, f, io);
634 if (!NT_STATUS_IS_OK(status)) {
638 /* form the lock context used for byte range locking and
640 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
641 if (!NT_STATUS_IS_OK(status)) {
645 status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle);
646 if (!NT_STATUS_IS_OK(status)) {
650 /* grab a lock on the open file record */
651 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
653 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
655 /* we were supposed to do a blocking lock, so something
657 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
661 if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
664 del_on_close = false;
667 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
668 oplock_level = OPLOCK_NONE;
669 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
670 oplock_level = OPLOCK_BATCH;
671 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
672 oplock_level = OPLOCK_EXCLUSIVE;
675 status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
676 share_access, access_mask, del_on_close,
677 io->generic.in.open_disposition,
678 false, oplock_level, &oplock_granted);
680 if (!NT_STATUS_IS_OK(status)) {
681 /* bad news, we must have hit a race - we don't delete the file
682 here as the most likely scenario is that someone else created
683 the file at the same time */
691 f->pending_list = NULL;
693 f->share_access = io->generic.in.share_access;
694 f->access_mask = access_mask;
695 f->impersonation = io->generic.in.impersonation;
696 f->notify_buffer = NULL;
699 f->handle->pvfs = pvfs;
700 f->handle->name = talloc_steal(f->handle, name);
702 f->handle->create_options = io->generic.in.create_options;
703 f->handle->seek_offset = 0;
704 f->handle->position = 0;
706 f->handle->oplock = NULL;
707 f->handle->have_opendb_entry = true;
708 f->handle->sticky_write_time = false;
709 f->handle->open_completed = false;
711 DLIST_ADD(pvfs->files.list, f);
713 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
714 oplock_granted = OPLOCK_BATCH;
715 } else if (oplock_granted != OPLOCK_NONE) {
716 status = pvfs_setup_oplock(f, oplock_granted);
717 if (!NT_STATUS_IS_OK(status)) {
723 /* setup a destructor to avoid file descriptor leaks on
724 abnormal termination */
725 talloc_set_destructor(f, pvfs_fnum_destructor);
726 talloc_set_destructor(f->handle, pvfs_handle_destructor);
728 io->generic.out.oplock_level = oplock_granted;
729 io->generic.out.file.ntvfs = f->ntvfs;
730 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
731 io->generic.out.create_time = name->dos.create_time;
732 io->generic.out.access_time = name->dos.access_time;
733 io->generic.out.write_time = name->dos.write_time;
734 io->generic.out.change_time = name->dos.change_time;
735 io->generic.out.attrib = name->dos.attrib;
736 io->generic.out.alloc_size = name->dos.alloc_size;
737 io->generic.out.size = name->st.st_size;
738 io->generic.out.file_type = FILE_TYPE_DISK;
739 io->generic.out.ipc_state = 0;
740 io->generic.out.is_directory = 0;
742 /* success - keep the file handle */
743 status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
744 if (!NT_STATUS_IS_OK(status)) {
748 f->handle->open_completed = true;
750 notify_trigger(pvfs->notify_context,
752 FILE_NOTIFY_CHANGE_FILE_NAME,
759 unlink(name->full_name);
764 state of a pending retry
766 struct pvfs_odb_retry {
767 struct ntvfs_module_context *ntvfs;
768 struct ntvfs_request *req;
769 DATA_BLOB odb_locking_key;
772 void (*callback)(struct pvfs_odb_retry *r,
773 struct ntvfs_module_context *ntvfs,
774 struct ntvfs_request *req,
777 enum pvfs_wait_notice reason);
780 /* destroy a pending request */
781 static int pvfs_odb_retry_destructor(struct pvfs_odb_retry *r)
783 struct pvfs_state *pvfs = r->ntvfs->private_data;
784 if (r->odb_locking_key.data) {
785 struct odb_lock *lck;
786 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
788 odb_remove_pending(lck, r);
795 static void pvfs_odb_retry_callback(void *_r, enum pvfs_wait_notice reason)
797 struct pvfs_odb_retry *r = talloc_get_type(_r, struct pvfs_odb_retry);
799 if (reason == PVFS_WAIT_EVENT) {
801 * The pending odb entry is already removed.
802 * We use a null locking key to indicate this
805 data_blob_free(&r->odb_locking_key);
808 r->callback(r, r->ntvfs, r->req, r->io, r->private_data, reason);
812 setup for a retry of a request that was rejected
813 by odb_open_file() or odb_can_open()
815 NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
816 struct ntvfs_request *req,
817 struct odb_lock *lck,
818 struct timeval end_time,
821 void (*callback)(struct pvfs_odb_retry *r,
822 struct ntvfs_module_context *ntvfs,
823 struct ntvfs_request *req,
826 enum pvfs_wait_notice reason))
828 struct pvfs_state *pvfs = ntvfs->private_data;
829 struct pvfs_odb_retry *r;
830 struct pvfs_wait *wait_handle;
833 r = talloc(req, struct pvfs_odb_retry);
834 NT_STATUS_HAVE_NO_MEMORY(r);
839 r->private_data = private_data;
840 r->callback = callback;
841 r->odb_locking_key = odb_get_key(r, lck);
842 if (r->odb_locking_key.data == NULL) {
843 return NT_STATUS_NO_MEMORY;
846 /* setup a pending lock */
847 status = odb_open_file_pending(lck, r);
848 if (!NT_STATUS_IS_OK(status)) {
854 talloc_set_destructor(r, pvfs_odb_retry_destructor);
856 wait_handle = pvfs_wait_message(pvfs, req,
857 MSG_PVFS_RETRY_OPEN, end_time,
858 pvfs_odb_retry_callback, r);
859 if (wait_handle == NULL) {
860 return NT_STATUS_NO_MEMORY;
863 talloc_steal(r, wait_handle);
865 talloc_steal(pvfs, r);
871 retry an open after a sharing violation
873 static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
874 struct ntvfs_module_context *ntvfs,
875 struct ntvfs_request *req,
878 enum pvfs_wait_notice reason)
880 union smb_open *io = talloc_get_type(_io, union smb_open);
883 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
884 just a bug in their server, but we better do the same */
885 if (reason == PVFS_WAIT_CANCEL) {
889 if (reason == PVFS_WAIT_TIMEOUT) {
890 /* if it timed out, then give the failure
893 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
894 req->async_states->send_fn(req);
900 /* try the open again, which could trigger another retry setup
901 if it wants to, so we have to unmark the async flag so we
902 will know if it does a second async reply */
903 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
905 status = pvfs_open(ntvfs, req, io);
906 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
907 /* the 2nd try also replied async, so we don't send
912 /* re-mark it async, just in case someone up the chain does
914 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
916 /* send the reply up the chain */
917 req->async_states->status = status;
918 req->async_states->send_fn(req);
923 special handling for openx DENY_DOS semantics
925 This function attempts a reference open using an existing handle. If its allowed,
926 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
927 open processing continues.
929 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
930 struct ntvfs_request *req, union smb_open *io,
931 struct pvfs_file *f, struct odb_lock *lck)
933 struct pvfs_state *pvfs = ntvfs->private_data;
934 struct pvfs_file *f2;
935 struct pvfs_filename *name;
938 /* search for an existing open with the right parameters. Note
939 the magic ntcreatex options flag, which is set in the
940 generic mapping code. This might look ugly, but its
941 actually pretty much now w2k does it internally as well.
943 If you look at the BASE-DENYDOS test you will see that a
944 DENY_DOS is a very special case, and in the right
945 circumstances you actually get the _same_ handle back
946 twice, rather than a new handle.
948 for (f2=pvfs->files.list;f2;f2=f2->next) {
950 f2->ntvfs->session_info == req->session_info &&
951 f2->ntvfs->smbpid == req->smbpid &&
952 (f2->handle->create_options &
953 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
954 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
955 (f2->access_mask & SEC_FILE_WRITE_DATA) &&
956 strcasecmp_m(f2->handle->name->original_name,
957 io->generic.in.fname)==0) {
963 return NT_STATUS_SHARING_VIOLATION;
966 /* quite an insane set of semantics ... */
967 if (is_exe_filename(io->generic.in.fname) &&
968 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
969 return NT_STATUS_SHARING_VIOLATION;
973 setup a reference to the existing handle
975 talloc_free(f->handle);
976 f->handle = talloc_reference(f, f2->handle);
980 name = f->handle->name;
982 io->generic.out.oplock_level = OPLOCK_NONE;
983 io->generic.out.file.ntvfs = f->ntvfs;
984 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
985 io->generic.out.create_time = name->dos.create_time;
986 io->generic.out.access_time = name->dos.access_time;
987 io->generic.out.write_time = name->dos.write_time;
988 io->generic.out.change_time = name->dos.change_time;
989 io->generic.out.attrib = name->dos.attrib;
990 io->generic.out.alloc_size = name->dos.alloc_size;
991 io->generic.out.size = name->st.st_size;
992 io->generic.out.file_type = FILE_TYPE_DISK;
993 io->generic.out.ipc_state = 0;
994 io->generic.out.is_directory = 0;
996 status = ntvfs_handle_set_backend_data(f->ntvfs, ntvfs, f);
997 NT_STATUS_NOT_OK_RETURN(status);
1005 setup for a open retry after a sharing violation
1007 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
1008 struct ntvfs_request *req,
1010 struct pvfs_file *f,
1011 struct odb_lock *lck,
1012 NTSTATUS parent_status)
1014 struct pvfs_state *pvfs = ntvfs->private_data;
1016 struct timeval end_time;
1018 if (io->generic.in.create_options &
1019 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
1020 /* see if we can satisfy the request using the special DENY_DOS
1022 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
1023 if (NT_STATUS_IS_OK(status)) {
1028 /* the retry should allocate a new file handle */
1031 if (NT_STATUS_EQUAL(parent_status, NT_STATUS_SHARING_VIOLATION)) {
1032 end_time = timeval_add(&req->statistics.request_time,
1033 0, pvfs->sharing_violation_delay);
1034 } else if (NT_STATUS_EQUAL(parent_status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
1035 end_time = timeval_add(&req->statistics.request_time,
1036 pvfs->oplock_break_timeout, 0);
1038 return NT_STATUS_INTERNAL_ERROR;
1041 return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
1042 pvfs_retry_open_sharing);
1048 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
1049 struct ntvfs_request *req, union smb_open *io)
1051 struct pvfs_state *pvfs = ntvfs->private_data;
1053 struct pvfs_filename *name;
1054 struct pvfs_file *f;
1055 struct ntvfs_handle *h;
1058 struct odb_lock *lck;
1059 uint32_t create_options;
1060 uint32_t share_access;
1061 uint32_t access_mask;
1063 bool stream_existed, stream_truncate=false;
1064 uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
1066 /* use the generic mapping code to avoid implementing all the
1067 different open calls. */
1068 if (io->generic.level != RAW_OPEN_GENERIC &&
1069 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
1070 return ntvfs_map_open(ntvfs, req, io);
1073 /* resolve the cifs name to a posix name */
1074 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
1075 PVFS_RESOLVE_STREAMS, &name);
1076 if (!NT_STATUS_IS_OK(status)) {
1080 /* directory opens are handled separately */
1081 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
1082 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
1083 return pvfs_open_directory(pvfs, req, name, io);
1086 /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
1087 open doesn't match */
1088 io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
1090 create_options = io->generic.in.create_options;
1091 share_access = io->generic.in.share_access;
1092 access_mask = io->generic.in.access_mask;
1094 /* certain create options are not allowed */
1095 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
1096 !(access_mask & SEC_STD_DELETE)) {
1097 return NT_STATUS_INVALID_PARAMETER;
1102 switch (io->generic.in.open_disposition) {
1103 case NTCREATEX_DISP_SUPERSEDE:
1104 case NTCREATEX_DISP_OVERWRITE_IF:
1105 if (name->stream_name == NULL) {
1108 stream_truncate = true;
1112 case NTCREATEX_DISP_OPEN:
1113 if (!name->stream_exists) {
1114 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1119 case NTCREATEX_DISP_OVERWRITE:
1120 if (!name->stream_exists) {
1121 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1123 if (name->stream_name == NULL) {
1126 stream_truncate = true;
1130 case NTCREATEX_DISP_CREATE:
1131 if (name->stream_exists) {
1132 return NT_STATUS_OBJECT_NAME_COLLISION;
1137 case NTCREATEX_DISP_OPEN_IF:
1142 return NT_STATUS_INVALID_PARAMETER;
1145 /* handle creating a new file separately */
1146 if (!name->exists) {
1147 status = pvfs_create_file(pvfs, req, name, io);
1148 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1152 /* we've hit a race - the file was created during this call */
1153 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
1157 /* try re-resolving the name */
1158 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
1159 if (!NT_STATUS_IS_OK(status)) {
1162 /* fall through to a normal open */
1165 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1166 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1167 return NT_STATUS_CANNOT_DELETE;
1170 /* check the security descriptor */
1171 status = pvfs_access_check(pvfs, req, name, &access_mask);
1172 if (!NT_STATUS_IS_OK(status)) {
1176 status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
1177 NT_STATUS_NOT_OK_RETURN(status);
1179 f = talloc(h, struct pvfs_file);
1181 return NT_STATUS_NO_MEMORY;
1184 f->handle = talloc(f, struct pvfs_file_handle);
1185 if (f->handle == NULL) {
1186 return NT_STATUS_NO_MEMORY;
1191 f->pending_list = NULL;
1193 f->share_access = io->generic.in.share_access;
1194 f->access_mask = access_mask;
1195 f->impersonation = io->generic.in.impersonation;
1196 f->notify_buffer = NULL;
1199 f->handle->pvfs = pvfs;
1201 f->handle->name = talloc_steal(f->handle, name);
1202 f->handle->create_options = io->generic.in.create_options;
1203 f->handle->seek_offset = 0;
1204 f->handle->position = 0;
1205 f->handle->mode = 0;
1206 f->handle->oplock = NULL;
1207 f->handle->have_opendb_entry = false;
1208 f->handle->sticky_write_time = false;
1209 f->handle->open_completed = false;
1211 /* form the lock context used for byte range locking and
1213 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
1214 if (!NT_STATUS_IS_OK(status)) {
1218 status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle);
1219 if (!NT_STATUS_IS_OK(status)) {
1223 /* get a lock on this file before the actual open */
1224 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1226 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1228 /* we were supposed to do a blocking lock, so something
1230 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1233 DLIST_ADD(pvfs->files.list, f);
1235 /* setup a destructor to avoid file descriptor leaks on
1236 abnormal termination */
1237 talloc_set_destructor(f, pvfs_fnum_destructor);
1238 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1241 * Only SMB2 takes care of the delete_on_close,
1244 if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE &&
1245 req->ctx->protocol == PROTOCOL_SMB2) {
1246 del_on_close = true;
1248 del_on_close = false;
1251 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1252 oplock_level = OPLOCK_NONE;
1253 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
1254 oplock_level = OPLOCK_BATCH;
1255 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
1256 oplock_level = OPLOCK_EXCLUSIVE;
1259 /* see if we are allowed to open at the same time as existing opens */
1260 status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
1261 share_access, access_mask, del_on_close,
1262 io->generic.in.open_disposition,
1263 false, oplock_level, &oplock_granted);
1266 * on a sharing violation we need to retry when the file is closed by
1267 * the other user, or after 1 second
1268 * on a non granted oplock we need to retry when the file is closed by
1269 * the other user, or after 30 seconds
1271 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
1272 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
1273 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1274 return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
1277 if (!NT_STATUS_IS_OK(status)) {
1282 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1283 oplock_granted = OPLOCK_BATCH;
1284 } else if (oplock_granted != OPLOCK_NONE) {
1285 status = pvfs_setup_oplock(f, oplock_granted);
1286 if (!NT_STATUS_IS_OK(status)) {
1292 f->handle->have_opendb_entry = true;
1294 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1300 /* do the actual open */
1301 fd = open(f->handle->name->full_name, flags);
1304 return pvfs_map_errno(f->pvfs, errno);
1309 stream_existed = name->stream_exists;
1311 /* if this was a stream create then create the stream as well */
1312 if (!name->stream_exists) {
1313 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1314 if (!NT_STATUS_IS_OK(status)) {
1318 if (stream_truncate) {
1319 status = pvfs_stream_truncate(pvfs, f->handle->name, fd, 0);
1320 if (!NT_STATUS_IS_OK(status)) {
1327 /* re-resolve the open fd */
1328 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1329 if (!NT_STATUS_IS_OK(status)) {
1334 if (f->handle->name->stream_id == 0 &&
1335 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1336 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1337 /* for overwrite we need to replace file permissions */
1338 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1339 mode_t mode = pvfs_fileperms(pvfs, attrib);
1340 if (fchmod(fd, mode) == -1) {
1342 return pvfs_map_errno(pvfs, errno);
1344 name->dos.attrib = attrib;
1345 status = pvfs_dosattrib_save(pvfs, name, fd);
1346 if (!NT_STATUS_IS_OK(status)) {
1354 status = ntvfs_handle_set_backend_data(h, ntvfs, f);
1355 NT_STATUS_NOT_OK_RETURN(status);
1357 /* mark the open as having completed fully, so delete on close
1359 f->handle->open_completed = true;
1361 io->generic.out.oplock_level = oplock_granted;
1362 io->generic.out.file.ntvfs = h;
1363 io->generic.out.create_action = stream_existed?
1364 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1365 io->generic.out.create_time = name->dos.create_time;
1366 io->generic.out.access_time = name->dos.access_time;
1367 io->generic.out.write_time = name->dos.write_time;
1368 io->generic.out.change_time = name->dos.change_time;
1369 io->generic.out.attrib = name->dos.attrib;
1370 io->generic.out.alloc_size = name->dos.alloc_size;
1371 io->generic.out.size = name->st.st_size;
1372 io->generic.out.file_type = FILE_TYPE_DISK;
1373 io->generic.out.ipc_state = 0;
1374 io->generic.out.is_directory = 0;
1376 return NT_STATUS_OK;
1383 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1384 struct ntvfs_request *req, union smb_close *io)
1386 struct pvfs_state *pvfs = ntvfs->private_data;
1387 struct pvfs_file *f;
1388 struct utimbuf unix_times;
1390 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1391 return NT_STATUS_DOS(ERRSRV, ERRerror);
1394 if (io->generic.level != RAW_CLOSE_CLOSE) {
1395 return ntvfs_map_close(ntvfs, req, io);
1398 f = pvfs_find_fd(pvfs, req, io->close.in.file.ntvfs);
1400 return NT_STATUS_INVALID_HANDLE;
1403 if (!null_time(io->close.in.write_time)) {
1404 unix_times.actime = 0;
1405 unix_times.modtime = io->close.in.write_time;
1406 utime(f->handle->name->full_name, &unix_times);
1407 } else if (f->handle->sticky_write_time) {
1408 unix_times.actime = 0;
1409 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1410 utime(f->handle->name->full_name, &unix_times);
1415 return NT_STATUS_OK;
1420 logoff - close all file descriptors open by a vuid
1422 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1423 struct ntvfs_request *req)
1425 struct pvfs_state *pvfs = ntvfs->private_data;
1426 struct pvfs_file *f, *next;
1428 for (f=pvfs->files.list;f;f=next) {
1430 if (f->ntvfs->session_info == req->session_info) {
1435 return NT_STATUS_OK;
1440 exit - close files for the current pid
1442 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1443 struct ntvfs_request *req)
1445 struct pvfs_state *pvfs = ntvfs->private_data;
1446 struct pvfs_file *f, *next;
1448 for (f=pvfs->files.list;f;f=next) {
1450 if (f->ntvfs->session_info == req->session_info &&
1451 f->ntvfs->smbpid == req->smbpid) {
1456 return NT_STATUS_OK;
1461 change the delete on close flag on an already open file
1463 NTSTATUS pvfs_set_delete_on_close(struct pvfs_state *pvfs,
1464 struct ntvfs_request *req,
1465 struct pvfs_file *f, bool del_on_close)
1467 struct odb_lock *lck;
1470 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) && del_on_close) {
1471 return NT_STATUS_CANNOT_DELETE;
1474 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
1475 !pvfs_directory_empty(pvfs, f->handle->name)) {
1476 return NT_STATUS_DIRECTORY_NOT_EMPTY;
1480 f->handle->create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1482 f->handle->create_options &= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1485 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1487 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1490 status = odb_set_delete_on_close(lck, del_on_close);
1499 determine if a file can be deleted, or if it is prevented by an
1502 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
1503 struct ntvfs_request *req,
1504 struct pvfs_filename *name,
1505 struct odb_lock **lckp)
1509 struct odb_lock *lck;
1510 uint32_t share_access;
1511 uint32_t access_mask;
1512 bool delete_on_close;
1514 status = pvfs_locking_key(name, name, &key);
1515 if (!NT_STATUS_IS_OK(status)) {
1516 return NT_STATUS_NO_MEMORY;
1519 lck = odb_lock(req, pvfs->odb_context, &key);
1521 DEBUG(0,("Unable to lock opendb for can_delete\n"));
1522 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1525 share_access = NTCREATEX_SHARE_ACCESS_READ |
1526 NTCREATEX_SHARE_ACCESS_WRITE |
1527 NTCREATEX_SHARE_ACCESS_DELETE;
1528 access_mask = SEC_STD_DELETE;
1529 delete_on_close = true;
1531 status = odb_can_open(lck, name->stream_id,
1532 share_access, access_mask, delete_on_close,
1535 if (NT_STATUS_IS_OK(status)) {
1536 status = pvfs_access_check_simple(pvfs, req, name, access_mask);
1540 * if it's a sharing violation or we got no oplock
1541 * only keep the lock if the caller requested access
1544 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
1545 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
1551 } else if (!NT_STATUS_IS_OK(status)) {
1564 determine if a file can be renamed, or if it is prevented by an
1567 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
1568 struct ntvfs_request *req,
1569 struct pvfs_filename *name,
1570 struct odb_lock **lckp)
1574 struct odb_lock *lck;
1575 uint32_t share_access;
1576 uint32_t access_mask;
1577 bool delete_on_close;
1579 status = pvfs_locking_key(name, name, &key);
1580 if (!NT_STATUS_IS_OK(status)) {
1581 return NT_STATUS_NO_MEMORY;
1584 lck = odb_lock(req, pvfs->odb_context, &key);
1586 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1587 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1590 share_access = NTCREATEX_SHARE_ACCESS_READ |
1591 NTCREATEX_SHARE_ACCESS_WRITE;
1592 access_mask = SEC_STD_DELETE;
1593 delete_on_close = false;
1595 status = odb_can_open(lck, name->stream_id,
1596 share_access, access_mask, delete_on_close,
1600 * if it's a sharing violation or we got no oplock
1601 * only keep the lock if the caller requested access
1604 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
1605 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
1611 } else if (!NT_STATUS_IS_OK(status)) {
1624 determine if file meta data can be accessed, or if it is prevented by an
1627 NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
1628 struct ntvfs_request *req,
1629 struct pvfs_filename *name)
1633 struct odb_lock *lck;
1634 uint32_t share_access;
1635 uint32_t access_mask;
1636 bool delete_on_close;
1638 status = pvfs_locking_key(name, name, &key);
1639 if (!NT_STATUS_IS_OK(status)) {
1640 return NT_STATUS_NO_MEMORY;
1643 lck = odb_lock(req, pvfs->odb_context, &key);
1645 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1646 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1649 share_access = NTCREATEX_SHARE_ACCESS_READ |
1650 NTCREATEX_SHARE_ACCESS_WRITE;
1652 delete_on_close = false;
1654 status = odb_can_open(lck, name->stream_id,
1655 share_access, access_mask, delete_on_close,
1658 if (!NT_STATUS_IS_OK(status)) {
1667 determine if delete on close is set on
1669 bool pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h,
1670 int *open_count, char **path)
1675 status = odb_get_delete_on_close(pvfs->odb_context, &h->odb_locking_key,
1676 &del_on_close, open_count, path);
1677 if (!NT_STATUS_IS_OK(status)) {
1678 DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
1682 return del_on_close;