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 2 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, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "vfs_posix.h"
25 #include "system/dir.h"
26 #include "system/time.h"
27 #include "lib/util/dlinklist.h"
28 #include "messaging/messaging.h"
29 #include "librpc/gen_ndr/xattr.h"
32 find open file handle given fnum
34 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
35 struct ntvfs_request *req, struct ntvfs_handle *h)
40 p = ntvfs_handle_get_backend_data(h, pvfs->ntvfs);
43 f = talloc_get_type(p, struct pvfs_file);
50 cleanup a open directory handle
52 static int pvfs_dir_handle_destructor(struct pvfs_file_handle *h)
57 if (h->name->stream_name == NULL &&
58 pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
61 status = pvfs_xattr_unlink_hook(h->pvfs, path);
62 if (!NT_STATUS_IS_OK(status)) {
63 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
64 path, nt_errstr(status)));
66 if (rmdir(path) != 0) {
67 DEBUG(0,("pvfs_dir_handle_destructor: failed to rmdir '%s' - %s\n",
68 path, strerror(errno)));
74 if (h->have_opendb_entry) {
78 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
80 DEBUG(0,("Unable to lock opendb for close\n"));
84 status = odb_close_file(lck, h);
85 if (!NT_STATUS_IS_OK(status)) {
86 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
87 h->name->full_name, nt_errstr(status)));
97 cleanup a open directory fnum
99 static int pvfs_dir_fnum_destructor(struct pvfs_file *f)
101 DLIST_REMOVE(f->pvfs->files.list, f);
102 ntvfs_handle_remove_backend_data(f->ntvfs, f->pvfs->ntvfs);
108 setup any EAs and the ACL on newly created files/directories
110 static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
111 struct ntvfs_request *req,
112 struct pvfs_filename *name,
113 int fd, struct pvfs_file *f,
118 /* setup any EAs that were asked for */
119 if (io->ntcreatex.in.ea_list) {
120 status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
121 io->ntcreatex.in.ea_list->num_eas,
122 io->ntcreatex.in.ea_list->eas);
123 if (!NT_STATUS_IS_OK(status)) {
128 /* setup an initial sec_desc if requested */
129 if (io->ntcreatex.in.sec_desc) {
130 union smb_setfileinfo set;
132 * TODO: set the full ACL!
133 * - vista denies the creation of the file with NT_STATUS_PRIVILEGE_NOT_HELD,
134 * when a SACL is present on the sd,
135 * but the user doesn't have SeSecurityPrivilege
138 set.set_secdesc.in.file.ntvfs = f->ntvfs;
139 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
140 set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
142 status = pvfs_acl_set(pvfs, req, name, fd, SEC_STD_WRITE_DAC, &set);
144 /* otherwise setup an inherited acl from the parent */
145 status = pvfs_acl_inherit(pvfs, req, name, fd);
152 form the lock context used for opendb locking. Note that we must
153 zero here to take account of possible padding on some architectures
155 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
156 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
162 ZERO_STRUCT(lock_context);
164 lock_context.device = name->st.st_dev;
165 lock_context.inode = name->st.st_ino;
167 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
168 if (key->data == NULL) {
169 return NT_STATUS_NO_MEMORY;
179 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
180 struct ntvfs_request *req,
181 struct pvfs_filename *name,
185 struct ntvfs_handle *h;
187 uint32_t create_action;
188 uint32_t access_mask = io->generic.in.access_mask;
189 struct odb_lock *lck;
191 uint32_t create_options;
192 uint32_t share_access;
194 create_options = io->generic.in.create_options;
195 share_access = io->generic.in.share_access;
197 if (name->stream_name) {
198 return NT_STATUS_NOT_A_DIRECTORY;
201 /* if the client says it must be a directory, and it isn't,
203 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
204 return NT_STATUS_NOT_A_DIRECTORY;
207 switch (io->generic.in.open_disposition) {
208 case NTCREATEX_DISP_OPEN_IF:
211 case NTCREATEX_DISP_OPEN:
213 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
217 case NTCREATEX_DISP_CREATE:
219 return NT_STATUS_OBJECT_NAME_COLLISION;
223 case NTCREATEX_DISP_OVERWRITE_IF:
224 case NTCREATEX_DISP_OVERWRITE:
225 case NTCREATEX_DISP_SUPERSEDE:
227 return NT_STATUS_INVALID_PARAMETER;
230 status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
231 NT_STATUS_NOT_OK_RETURN(status);
233 f = talloc(h, struct pvfs_file);
235 return NT_STATUS_NO_MEMORY;
238 f->handle = talloc(f, struct pvfs_file_handle);
239 if (f->handle == NULL) {
240 return NT_STATUS_NO_MEMORY;
244 /* check the security descriptor */
245 status = pvfs_access_check(pvfs, req, name, &access_mask);
247 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
249 if (!NT_STATUS_IS_OK(status)) {
255 f->pending_list = NULL;
257 f->share_access = io->generic.in.share_access;
258 f->impersonation = io->generic.in.impersonation;
259 f->access_mask = access_mask;
260 f->brl_handle = NULL;
261 f->notify_buffer = NULL;
264 f->handle->pvfs = pvfs;
265 f->handle->name = talloc_steal(f->handle, name);
267 f->handle->odb_locking_key = data_blob(NULL, 0);
268 f->handle->create_options = io->generic.in.create_options;
269 f->handle->seek_offset = 0;
270 f->handle->position = 0;
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, f->handle->name->stream_id,
301 share_access, access_mask, del_on_close,
302 name->full_name, OPLOCK_NONE, NULL);
304 if (!NT_STATUS_IS_OK(status)) {
309 f->handle->have_opendb_entry = True;
312 DLIST_ADD(pvfs->files.list, f);
314 /* setup destructors to avoid leaks on abnormal termination */
315 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
316 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
319 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
320 mode_t mode = pvfs_fileperms(pvfs, attrib);
322 if (mkdir(name->full_name, mode) == -1) {
323 return pvfs_map_errno(pvfs,errno);
326 pvfs_xattr_unlink_hook(pvfs, name->full_name);
328 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
329 if (!NT_STATUS_IS_OK(status)) {
333 status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, f, io);
334 if (!NT_STATUS_IS_OK(status)) {
338 /* form the lock context used for opendb locking */
339 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
340 if (!NT_STATUS_IS_OK(status)) {
344 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
346 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
348 /* we were supposed to do a blocking lock, so something
350 return NT_STATUS_INTERNAL_DB_CORRUPTION;
353 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
354 share_access, access_mask, del_on_close,
355 name->full_name, OPLOCK_NONE, NULL);
357 if (!NT_STATUS_IS_OK(status)) {
361 f->handle->have_opendb_entry = True;
363 create_action = NTCREATEX_ACTION_CREATED;
365 notify_trigger(pvfs->notify_context,
367 FILE_NOTIFY_CHANGE_DIR_NAME,
370 create_action = NTCREATEX_ACTION_EXISTED;
374 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
377 /* the open succeeded, keep this handle permanently */
378 status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
379 if (!NT_STATUS_IS_OK(status)) {
383 f->handle->open_completed = True;
385 io->generic.out.oplock_level = OPLOCK_NONE;
386 io->generic.out.file.ntvfs = h;
387 io->generic.out.create_action = create_action;
388 io->generic.out.create_time = name->dos.create_time;
389 io->generic.out.access_time = name->dos.access_time;
390 io->generic.out.write_time = name->dos.write_time;
391 io->generic.out.change_time = name->dos.change_time;
392 io->generic.out.attrib = name->dos.attrib;
393 io->generic.out.alloc_size = name->dos.alloc_size;
394 io->generic.out.size = name->st.st_size;
395 io->generic.out.file_type = FILE_TYPE_DISK;
396 io->generic.out.ipc_state = 0;
397 io->generic.out.is_directory = 1;
402 rmdir(name->full_name);
407 destroy a struct pvfs_file_handle
409 static int pvfs_handle_destructor(struct pvfs_file_handle *h)
414 /* the write time is no longer sticky */
415 if (h->sticky_write_time) {
417 status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
418 if (NT_STATUS_IS_OK(status)) {
419 h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
420 pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
424 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
425 h->name->stream_name) {
427 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
428 if (!NT_STATUS_IS_OK(status)) {
429 DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
430 h->name->stream_name, h->name->full_name));
435 if (close(h->fd) != 0) {
436 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
437 h->fd, h->name->full_name, strerror(errno)));
442 if (h->name->stream_name == NULL &&
444 pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
447 status = pvfs_xattr_unlink_hook(h->pvfs, path);
448 if (!NT_STATUS_IS_OK(status)) {
449 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
450 path, nt_errstr(status)));
452 if (unlink(path) != 0) {
453 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
454 path, strerror(errno)));
456 notify_trigger(h->pvfs->notify_context,
457 NOTIFY_ACTION_REMOVED,
458 FILE_NOTIFY_CHANGE_FILE_NAME,
465 if (h->have_opendb_entry) {
466 struct odb_lock *lck;
469 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
471 DEBUG(0,("Unable to lock opendb for close\n"));
475 status = odb_close_file(lck, h);
476 if (!NT_STATUS_IS_OK(status)) {
477 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
478 h->name->full_name, nt_errstr(status)));
489 destroy a struct pvfs_file
491 static int pvfs_fnum_destructor(struct pvfs_file *f)
493 DLIST_REMOVE(f->pvfs->files.list, f);
494 pvfs_lock_close(f->pvfs, f);
495 ntvfs_handle_remove_backend_data(f->ntvfs, f->pvfs->ntvfs);
502 form the lock context used for byte range locking. This is separate
503 from the locking key used for opendb locking as it needs to take
504 account of file streams (each stream is a separate byte range
507 static NTSTATUS pvfs_brl_locking_handle(TALLOC_CTX *mem_ctx,
508 struct pvfs_filename *name,
509 struct ntvfs_handle *ntvfs,
510 struct brl_handle **_h)
512 DATA_BLOB odb_key, key;
514 struct brl_handle *h;
516 status = pvfs_locking_key(name, mem_ctx, &odb_key);
517 NT_STATUS_NOT_OK_RETURN(status);
519 if (name->stream_name == NULL) {
522 key = data_blob_talloc(mem_ctx, NULL,
523 odb_key.length + strlen(name->stream_name) + 1);
524 NT_STATUS_HAVE_NO_MEMORY(key.data);
525 memcpy(key.data, odb_key.data, odb_key.length);
526 memcpy(key.data + odb_key.length,
527 name->stream_name, strlen(name->stream_name) + 1);
528 data_blob_free(&odb_key);
531 h = brl_create_handle(mem_ctx, ntvfs, &key);
532 NT_STATUS_HAVE_NO_MEMORY(h);
541 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
542 struct ntvfs_request *req,
543 struct pvfs_filename *name,
548 struct ntvfs_handle *h;
550 struct odb_lock *lck;
551 uint32_t create_options = io->generic.in.create_options;
552 uint32_t share_access = io->generic.in.share_access;
553 uint32_t access_mask = io->generic.in.access_mask;
557 struct pvfs_filename *parent;
558 uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
560 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
561 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
562 return NT_STATUS_CANNOT_DELETE;
565 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
566 NT_STATUS_NOT_OK_RETURN(status);
568 /* check that the parent isn't opened with delete on close set */
569 status = pvfs_resolve_parent(pvfs, req, name, &parent);
570 if (NT_STATUS_IS_OK(status)) {
571 DATA_BLOB locking_key;
572 status = pvfs_locking_key(parent, req, &locking_key);
573 NT_STATUS_NOT_OK_RETURN(status);
574 status = odb_get_delete_on_close(pvfs->odb_context, &locking_key,
575 &del_on_close, NULL, NULL);
576 NT_STATUS_NOT_OK_RETURN(status);
578 return NT_STATUS_DELETE_PENDING;
582 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
588 status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
589 NT_STATUS_NOT_OK_RETURN(status);
591 f = talloc(h, struct pvfs_file);
592 NT_STATUS_HAVE_NO_MEMORY(f);
594 f->handle = talloc(f, struct pvfs_file_handle);
595 NT_STATUS_HAVE_NO_MEMORY(f->handle);
597 attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
598 mode = pvfs_fileperms(pvfs, attrib);
600 /* create the file */
601 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
603 return pvfs_map_errno(pvfs, errno);
606 pvfs_xattr_unlink_hook(pvfs, name->full_name);
608 /* if this was a stream create then create the stream as well */
609 if (name->stream_name) {
610 status = pvfs_stream_create(pvfs, name, fd);
611 if (!NT_STATUS_IS_OK(status)) {
617 /* re-resolve the open fd */
618 status = pvfs_resolve_name_fd(pvfs, fd, name);
619 if (!NT_STATUS_IS_OK(status)) {
624 name->dos.attrib = attrib;
625 status = pvfs_dosattrib_save(pvfs, name, fd);
626 if (!NT_STATUS_IS_OK(status)) {
631 status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, f, io);
632 if (!NT_STATUS_IS_OK(status)) {
636 /* form the lock context used for byte range locking and
638 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
639 if (!NT_STATUS_IS_OK(status)) {
643 status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle);
644 if (!NT_STATUS_IS_OK(status)) {
648 /* grab a lock on the open file record */
649 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
651 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
653 /* we were supposed to do a blocking lock, so something
655 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
659 if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
662 del_on_close = False;
665 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
666 oplock_level = OPLOCK_NONE;
667 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
668 oplock_level = OPLOCK_BATCH;
669 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
670 oplock_level = OPLOCK_EXCLUSIVE;
673 status = odb_open_file(lck, f->handle, name->stream_id,
674 share_access, access_mask, del_on_close,
675 name->full_name, oplock_level, &oplock_granted);
677 if (!NT_STATUS_IS_OK(status)) {
678 /* bad news, we must have hit a race - we don't delete the file
679 here as the most likely scenario is that someone else created
680 the file at the same time */
685 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
686 oplock_granted = OPLOCK_BATCH;
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->have_opendb_entry = True;
707 f->handle->sticky_write_time = False;
708 f->handle->open_completed = False;
710 DLIST_ADD(pvfs->files.list, f);
712 /* setup a destructor to avoid file descriptor leaks on
713 abnormal termination */
714 talloc_set_destructor(f, pvfs_fnum_destructor);
715 talloc_set_destructor(f->handle, pvfs_handle_destructor);
717 io->generic.out.oplock_level = oplock_granted;
718 io->generic.out.file.ntvfs = f->ntvfs;
719 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
720 io->generic.out.create_time = name->dos.create_time;
721 io->generic.out.access_time = name->dos.access_time;
722 io->generic.out.write_time = name->dos.write_time;
723 io->generic.out.change_time = name->dos.change_time;
724 io->generic.out.attrib = name->dos.attrib;
725 io->generic.out.alloc_size = name->dos.alloc_size;
726 io->generic.out.size = name->st.st_size;
727 io->generic.out.file_type = FILE_TYPE_DISK;
728 io->generic.out.ipc_state = 0;
729 io->generic.out.is_directory = 0;
731 /* success - keep the file handle */
732 status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
733 if (!NT_STATUS_IS_OK(status)) {
737 f->handle->open_completed = True;
739 notify_trigger(pvfs->notify_context,
741 FILE_NOTIFY_CHANGE_FILE_NAME,
748 unlink(name->full_name);
754 state of a pending open retry
756 struct pvfs_open_retry {
757 struct ntvfs_module_context *ntvfs;
758 struct ntvfs_request *req;
761 DATA_BLOB odb_locking_key;
764 /* destroy a pending open request */
765 static int pvfs_retry_destructor(struct pvfs_open_retry *r)
767 struct pvfs_state *pvfs = r->ntvfs->private_data;
768 if (r->odb_locking_key.data) {
769 struct odb_lock *lck;
770 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
772 odb_remove_pending(lck, r);
782 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
784 struct pvfs_open_retry *r = private;
785 struct ntvfs_module_context *ntvfs = r->ntvfs;
786 struct ntvfs_request *req = r->req;
787 union smb_open *io = r->io;
790 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
791 just a bug in their server, but we better do the same */
792 if (reason == PVFS_WAIT_CANCEL) {
796 talloc_free(r->wait_handle);
798 if (reason == PVFS_WAIT_TIMEOUT) {
799 /* if it timed out, then give the failure
802 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
803 req->async_states->send_fn(req);
807 /* the pending odb entry is already removed. We use a null locking
808 key to indicate this */
809 data_blob_free(&r->odb_locking_key);
812 /* try the open again, which could trigger another retry setup
813 if it wants to, so we have to unmark the async flag so we
814 will know if it does a second async reply */
815 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
817 status = pvfs_open(ntvfs, req, io);
818 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
819 /* the 2nd try also replied async, so we don't send
824 /* re-mark it async, just in case someone up the chain does
826 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
828 /* send the reply up the chain */
829 req->async_states->status = status;
830 req->async_states->send_fn(req);
835 special handling for openx DENY_DOS semantics
837 This function attempts a reference open using an existing handle. If its allowed,
838 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
839 open processing continues.
841 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
842 struct ntvfs_request *req, union smb_open *io,
843 struct pvfs_file *f, struct odb_lock *lck)
845 struct pvfs_state *pvfs = ntvfs->private_data;
846 struct pvfs_file *f2;
847 struct pvfs_filename *name;
850 /* search for an existing open with the right parameters. Note
851 the magic ntcreatex options flag, which is set in the
852 generic mapping code. This might look ugly, but its
853 actually pretty much now w2k does it internally as well.
855 If you look at the BASE-DENYDOS test you will see that a
856 DENY_DOS is a very special case, and in the right
857 circumstances you actually get the _same_ handle back
858 twice, rather than a new handle.
860 for (f2=pvfs->files.list;f2;f2=f2->next) {
862 f2->ntvfs->session_info == req->session_info &&
863 f2->ntvfs->smbpid == req->smbpid &&
864 (f2->handle->create_options &
865 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
866 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
867 (f2->access_mask & SEC_FILE_WRITE_DATA) &&
868 strcasecmp_m(f2->handle->name->original_name,
869 io->generic.in.fname)==0) {
875 return NT_STATUS_SHARING_VIOLATION;
878 /* quite an insane set of semantics ... */
879 if (is_exe_filename(io->generic.in.fname) &&
880 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
881 return NT_STATUS_SHARING_VIOLATION;
885 setup a reference to the existing handle
887 talloc_free(f->handle);
888 f->handle = talloc_reference(f, f2->handle);
892 name = f->handle->name;
894 io->generic.out.oplock_level = OPLOCK_NONE;
895 io->generic.out.file.ntvfs = f->ntvfs;
896 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
897 io->generic.out.create_time = name->dos.create_time;
898 io->generic.out.access_time = name->dos.access_time;
899 io->generic.out.write_time = name->dos.write_time;
900 io->generic.out.change_time = name->dos.change_time;
901 io->generic.out.attrib = name->dos.attrib;
902 io->generic.out.alloc_size = name->dos.alloc_size;
903 io->generic.out.size = name->st.st_size;
904 io->generic.out.file_type = FILE_TYPE_DISK;
905 io->generic.out.ipc_state = 0;
906 io->generic.out.is_directory = 0;
908 status = ntvfs_handle_set_backend_data(f->ntvfs, ntvfs, f);
909 NT_STATUS_NOT_OK_RETURN(status);
917 setup for a open retry after a sharing violation
919 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
920 struct ntvfs_request *req,
923 struct odb_lock *lck)
925 struct pvfs_state *pvfs = ntvfs->private_data;
926 struct pvfs_open_retry *r;
928 struct timeval end_time;
930 if (io->generic.in.create_options &
931 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
932 /* see if we can satisfy the request using the special DENY_DOS
934 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
935 if (NT_STATUS_IS_OK(status)) {
940 r = talloc(req, struct pvfs_open_retry);
942 return NT_STATUS_NO_MEMORY;
948 r->odb_locking_key = data_blob_talloc(r,
949 f->handle->odb_locking_key.data,
950 f->handle->odb_locking_key.length);
952 end_time = timeval_add(&req->statistics.request_time, 0, pvfs->sharing_violation_delay);
954 /* setup a pending lock */
955 status = odb_open_file_pending(lck, r);
956 if (!NT_STATUS_IS_OK(status)) {
963 talloc_set_destructor(r, pvfs_retry_destructor);
965 r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
967 if (r->wait_handle == NULL) {
968 return NT_STATUS_NO_MEMORY;
971 talloc_steal(pvfs, r);
979 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
980 struct ntvfs_request *req, union smb_open *io)
982 struct pvfs_state *pvfs = ntvfs->private_data;
984 struct pvfs_filename *name;
986 struct ntvfs_handle *h;
989 struct odb_lock *lck;
990 uint32_t create_options;
991 uint32_t share_access;
992 uint32_t access_mask;
994 BOOL stream_existed, stream_truncate=False;
995 uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
997 /* use the generic mapping code to avoid implementing all the
998 different open calls. */
999 if (io->generic.level != RAW_OPEN_GENERIC &&
1000 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
1001 return ntvfs_map_open(ntvfs, req, io);
1004 /* resolve the cifs name to a posix name */
1005 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
1006 PVFS_RESOLVE_STREAMS, &name);
1007 if (!NT_STATUS_IS_OK(status)) {
1011 /* directory opens are handled separately */
1012 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
1013 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
1014 return pvfs_open_directory(pvfs, req, name, io);
1017 /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
1018 open doesn't match */
1019 io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
1021 create_options = io->generic.in.create_options;
1022 share_access = io->generic.in.share_access;
1023 access_mask = io->generic.in.access_mask;
1025 /* certain create options are not allowed */
1026 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
1027 !(access_mask & SEC_STD_DELETE)) {
1028 return NT_STATUS_INVALID_PARAMETER;
1033 switch (io->generic.in.open_disposition) {
1034 case NTCREATEX_DISP_SUPERSEDE:
1035 case NTCREATEX_DISP_OVERWRITE_IF:
1036 if (name->stream_name == NULL) {
1039 stream_truncate = True;
1043 case NTCREATEX_DISP_OPEN:
1044 if (!name->stream_exists) {
1045 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1050 case NTCREATEX_DISP_OVERWRITE:
1051 if (!name->stream_exists) {
1052 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1054 if (name->stream_name == NULL) {
1057 stream_truncate = True;
1061 case NTCREATEX_DISP_CREATE:
1062 if (name->stream_exists) {
1063 return NT_STATUS_OBJECT_NAME_COLLISION;
1068 case NTCREATEX_DISP_OPEN_IF:
1073 return NT_STATUS_INVALID_PARAMETER;
1076 /* handle creating a new file separately */
1077 if (!name->exists) {
1078 status = pvfs_create_file(pvfs, req, name, io);
1079 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1083 /* we've hit a race - the file was created during this call */
1084 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
1088 /* try re-resolving the name */
1089 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
1090 if (!NT_STATUS_IS_OK(status)) {
1093 /* fall through to a normal open */
1096 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1097 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1098 return NT_STATUS_CANNOT_DELETE;
1101 /* check the security descriptor */
1102 status = pvfs_access_check(pvfs, req, name, &access_mask);
1103 if (!NT_STATUS_IS_OK(status)) {
1107 status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
1108 NT_STATUS_NOT_OK_RETURN(status);
1110 f = talloc(h, struct pvfs_file);
1112 return NT_STATUS_NO_MEMORY;
1115 f->handle = talloc(f, struct pvfs_file_handle);
1116 if (f->handle == NULL) {
1117 return NT_STATUS_NO_MEMORY;
1122 f->pending_list = NULL;
1124 f->share_access = io->generic.in.share_access;
1125 f->access_mask = access_mask;
1126 f->impersonation = io->generic.in.impersonation;
1127 f->notify_buffer = NULL;
1130 f->handle->pvfs = pvfs;
1132 f->handle->name = talloc_steal(f->handle, name);
1133 f->handle->create_options = io->generic.in.create_options;
1134 f->handle->seek_offset = 0;
1135 f->handle->position = 0;
1136 f->handle->mode = 0;
1137 f->handle->have_opendb_entry = False;
1138 f->handle->sticky_write_time = False;
1139 f->handle->open_completed = False;
1141 /* form the lock context used for byte range locking and
1143 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
1144 if (!NT_STATUS_IS_OK(status)) {
1148 status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle);
1149 if (!NT_STATUS_IS_OK(status)) {
1153 /* get a lock on this file before the actual open */
1154 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1156 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1158 /* we were supposed to do a blocking lock, so something
1160 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1163 DLIST_ADD(pvfs->files.list, f);
1165 /* setup a destructor to avoid file descriptor leaks on
1166 abnormal termination */
1167 talloc_set_destructor(f, pvfs_fnum_destructor);
1168 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1171 * Only SMB2 takes care of the delete_on_close,
1174 if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE &&
1175 req->ctx->protocol == PROTOCOL_SMB2) {
1176 del_on_close = True;
1178 del_on_close = False;
1181 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1182 oplock_level = OPLOCK_NONE;
1183 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
1184 oplock_level = OPLOCK_BATCH;
1185 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
1186 oplock_level = OPLOCK_EXCLUSIVE;
1189 /* see if we are allowed to open at the same time as existing opens */
1190 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
1191 share_access, access_mask, del_on_close,
1192 name->full_name, oplock_level, &oplock_granted);
1194 /* on a sharing violation we need to retry when the file is closed by
1195 the other user, or after 1 second */
1196 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1197 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1198 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1201 if (!NT_STATUS_IS_OK(status)) {
1206 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1207 oplock_granted = OPLOCK_BATCH;
1210 f->handle->have_opendb_entry = True;
1212 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1218 /* do the actual open */
1219 fd = open(f->handle->name->full_name, flags);
1222 return pvfs_map_errno(f->pvfs, errno);
1227 stream_existed = name->stream_exists;
1229 /* if this was a stream create then create the stream as well */
1230 if (!name->stream_exists) {
1231 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1232 if (!NT_STATUS_IS_OK(status)) {
1236 if (stream_truncate) {
1237 status = pvfs_stream_truncate(pvfs, f->handle->name, fd, 0);
1238 if (!NT_STATUS_IS_OK(status)) {
1245 /* re-resolve the open fd */
1246 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1247 if (!NT_STATUS_IS_OK(status)) {
1252 if (f->handle->name->stream_id == 0 &&
1253 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1254 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1255 /* for overwrite we need to replace file permissions */
1256 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1257 mode_t mode = pvfs_fileperms(pvfs, attrib);
1258 if (fchmod(fd, mode) == -1) {
1260 return pvfs_map_errno(pvfs, errno);
1262 name->dos.attrib = attrib;
1263 status = pvfs_dosattrib_save(pvfs, name, fd);
1264 if (!NT_STATUS_IS_OK(status)) {
1272 status = ntvfs_handle_set_backend_data(h, ntvfs, f);
1273 NT_STATUS_NOT_OK_RETURN(status);
1275 /* mark the open as having completed fully, so delete on close
1277 f->handle->open_completed = True;
1279 io->generic.out.oplock_level = oplock_granted;
1280 io->generic.out.file.ntvfs = h;
1281 io->generic.out.create_action = stream_existed?
1282 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1283 io->generic.out.create_time = name->dos.create_time;
1284 io->generic.out.access_time = name->dos.access_time;
1285 io->generic.out.write_time = name->dos.write_time;
1286 io->generic.out.change_time = name->dos.change_time;
1287 io->generic.out.attrib = name->dos.attrib;
1288 io->generic.out.alloc_size = name->dos.alloc_size;
1289 io->generic.out.size = name->st.st_size;
1290 io->generic.out.file_type = FILE_TYPE_DISK;
1291 io->generic.out.ipc_state = 0;
1292 io->generic.out.is_directory = 0;
1294 return NT_STATUS_OK;
1301 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1302 struct ntvfs_request *req, union smb_close *io)
1304 struct pvfs_state *pvfs = ntvfs->private_data;
1305 struct pvfs_file *f;
1306 struct utimbuf unix_times;
1308 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1309 return NT_STATUS_DOS(ERRSRV, ERRerror);
1312 if (io->generic.level != RAW_CLOSE_CLOSE) {
1313 return ntvfs_map_close(ntvfs, req, io);
1316 f = pvfs_find_fd(pvfs, req, io->close.in.file.ntvfs);
1318 return NT_STATUS_INVALID_HANDLE;
1321 if (!null_time(io->close.in.write_time)) {
1322 unix_times.actime = 0;
1323 unix_times.modtime = io->close.in.write_time;
1324 utime(f->handle->name->full_name, &unix_times);
1325 } else if (f->handle->sticky_write_time) {
1326 unix_times.actime = 0;
1327 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1328 utime(f->handle->name->full_name, &unix_times);
1333 return NT_STATUS_OK;
1338 logoff - close all file descriptors open by a vuid
1340 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1341 struct ntvfs_request *req)
1343 struct pvfs_state *pvfs = ntvfs->private_data;
1344 struct pvfs_file *f, *next;
1346 for (f=pvfs->files.list;f;f=next) {
1348 if (f->ntvfs->session_info == req->session_info) {
1353 return NT_STATUS_OK;
1358 exit - close files for the current pid
1360 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1361 struct ntvfs_request *req)
1363 struct pvfs_state *pvfs = ntvfs->private_data;
1364 struct pvfs_file *f, *next;
1366 for (f=pvfs->files.list;f;f=next) {
1368 if (f->ntvfs->session_info == req->session_info &&
1369 f->ntvfs->smbpid == req->smbpid) {
1374 return NT_STATUS_OK;
1379 change the delete on close flag on an already open file
1381 NTSTATUS pvfs_set_delete_on_close(struct pvfs_state *pvfs,
1382 struct ntvfs_request *req,
1383 struct pvfs_file *f, BOOL del_on_close)
1385 struct odb_lock *lck;
1388 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) && del_on_close) {
1389 return NT_STATUS_CANNOT_DELETE;
1392 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
1393 !pvfs_directory_empty(pvfs, f->handle->name)) {
1394 return NT_STATUS_DIRECTORY_NOT_EMPTY;
1398 f->handle->create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1400 f->handle->create_options &= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1403 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1405 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1408 status = odb_set_delete_on_close(lck, del_on_close);
1417 determine if a file can be deleted, or if it is prevented by an
1420 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
1421 struct ntvfs_request *req,
1422 struct pvfs_filename *name,
1423 struct odb_lock **lckp)
1427 struct odb_lock *lck;
1429 status = pvfs_locking_key(name, name, &key);
1430 if (!NT_STATUS_IS_OK(status)) {
1431 return NT_STATUS_NO_MEMORY;
1434 lck = odb_lock(req, pvfs->odb_context, &key);
1436 DEBUG(0,("Unable to lock opendb for can_delete\n"));
1437 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1440 status = odb_can_open(lck,
1441 NTCREATEX_SHARE_ACCESS_READ |
1442 NTCREATEX_SHARE_ACCESS_WRITE |
1443 NTCREATEX_SHARE_ACCESS_DELETE,
1444 NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
1447 if (NT_STATUS_IS_OK(status)) {
1448 status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
1451 if (!NT_STATUS_IS_OK(status)) {
1454 } else if (lckp != NULL) {
1462 determine if a file can be renamed, or if it is prevented by an
1465 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
1466 struct ntvfs_request *req,
1467 struct pvfs_filename *name,
1468 struct odb_lock **lckp)
1472 struct odb_lock *lck;
1474 status = pvfs_locking_key(name, name, &key);
1475 if (!NT_STATUS_IS_OK(status)) {
1476 return NT_STATUS_NO_MEMORY;
1479 lck = odb_lock(req, pvfs->odb_context, &key);
1481 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1482 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1485 status = odb_can_open(lck,
1486 NTCREATEX_SHARE_ACCESS_READ |
1487 NTCREATEX_SHARE_ACCESS_WRITE,
1491 if (!NT_STATUS_IS_OK(status)) {
1494 } else if (lckp != NULL) {
1502 determine if file meta data can be accessed, or if it is prevented by an
1505 NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
1506 struct ntvfs_request *req,
1507 struct pvfs_filename *name)
1511 struct odb_lock *lck;
1513 status = pvfs_locking_key(name, name, &key);
1514 if (!NT_STATUS_IS_OK(status)) {
1515 return NT_STATUS_NO_MEMORY;
1518 lck = odb_lock(req, pvfs->odb_context, &key);
1520 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1521 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1524 status = odb_can_open(lck,
1525 NTCREATEX_SHARE_ACCESS_READ |
1526 NTCREATEX_SHARE_ACCESS_WRITE,
1534 determine if delete on close is set on
1536 BOOL pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h,
1537 int *open_count, char **path)
1542 status = odb_get_delete_on_close(pvfs->odb_context, &h->odb_locking_key,
1543 &del_on_close, open_count, path);
1544 if (!NT_STATUS_IS_OK(status)) {
1545 DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
1549 return del_on_close;