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 "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);
46 if (req->session_info != f->session_info) {
47 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for handle %p\n",h));
55 cleanup a open directory handle
57 static int pvfs_dir_handle_destructor(struct pvfs_file_handle *h)
62 if (h->name->stream_name == NULL &&
63 pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
66 status = pvfs_xattr_unlink_hook(h->pvfs, path);
67 if (!NT_STATUS_IS_OK(status)) {
68 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
69 path, nt_errstr(status)));
71 if (rmdir(path) != 0) {
72 DEBUG(0,("pvfs_dir_handle_destructor: failed to rmdir '%s' - %s\n",
73 path, strerror(errno)));
79 if (h->have_opendb_entry) {
83 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
85 DEBUG(0,("Unable to lock opendb for close\n"));
89 status = odb_close_file(lck, h);
90 if (!NT_STATUS_IS_OK(status)) {
91 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
92 h->name->full_name, nt_errstr(status)));
102 cleanup a open directory fnum
104 static int pvfs_dir_fnum_destructor(struct pvfs_file *f)
106 DLIST_REMOVE(f->pvfs->files.list, f);
107 ntvfs_handle_remove_backend_data(f->ntvfs, f->pvfs->ntvfs);
113 setup any EAs and the ACL on newly created files/directories
115 static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
116 struct ntvfs_request *req,
117 struct pvfs_filename *name,
118 int fd, struct pvfs_file *f,
123 /* setup any EAs that were asked for */
124 if (io->ntcreatex.in.ea_list) {
125 status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
126 io->ntcreatex.in.ea_list->num_eas,
127 io->ntcreatex.in.ea_list->eas);
128 if (!NT_STATUS_IS_OK(status)) {
133 /* setup an initial sec_desc if requested */
134 if (io->ntcreatex.in.sec_desc) {
135 union smb_setfileinfo set;
137 * TODO: set the full ACL!
138 * - vista denies the creation of the file with NT_STATUS_PRIVILEGE_NOT_HELD,
139 * when a SACL is present on the sd,
140 * but the user doesn't have SeSecurityPrivilege
143 set.set_secdesc.in.file.ntvfs = f->ntvfs;
144 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
145 set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
147 status = pvfs_acl_set(pvfs, req, name, fd, SEC_STD_WRITE_DAC, &set);
149 /* otherwise setup an inherited acl from the parent */
150 status = pvfs_acl_inherit(pvfs, req, name, fd);
157 form the lock context used for opendb locking. Note that we must
158 zero here to take account of possible padding on some architectures
160 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
161 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
167 ZERO_STRUCT(lock_context);
169 lock_context.device = name->st.st_dev;
170 lock_context.inode = name->st.st_ino;
172 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
173 if (key->data == NULL) {
174 return NT_STATUS_NO_MEMORY;
184 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
185 struct ntvfs_request *req,
186 struct pvfs_filename *name,
190 struct ntvfs_handle *h;
192 uint32_t create_action;
193 uint32_t access_mask = io->generic.in.access_mask;
194 struct odb_lock *lck;
196 uint32_t create_options;
197 uint32_t share_access;
199 create_options = io->generic.in.create_options;
200 share_access = io->generic.in.share_access;
202 if (name->stream_name) {
203 return NT_STATUS_NOT_A_DIRECTORY;
206 /* if the client says it must be a directory, and it isn't,
208 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
209 return NT_STATUS_NOT_A_DIRECTORY;
212 switch (io->generic.in.open_disposition) {
213 case NTCREATEX_DISP_OPEN_IF:
216 case NTCREATEX_DISP_OPEN:
218 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
222 case NTCREATEX_DISP_CREATE:
224 return NT_STATUS_OBJECT_NAME_COLLISION;
228 case NTCREATEX_DISP_OVERWRITE_IF:
229 case NTCREATEX_DISP_OVERWRITE:
230 case NTCREATEX_DISP_SUPERSEDE:
232 return NT_STATUS_INVALID_PARAMETER;
235 status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
236 NT_STATUS_NOT_OK_RETURN(status);
238 f = talloc(h, struct pvfs_file);
240 return NT_STATUS_NO_MEMORY;
243 f->handle = talloc(f, struct pvfs_file_handle);
244 if (f->handle == NULL) {
245 return NT_STATUS_NO_MEMORY;
249 /* check the security descriptor */
250 status = pvfs_access_check(pvfs, req, name, &access_mask);
252 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
254 if (!NT_STATUS_IS_OK(status)) {
259 f->session_info = req->session_info;
260 f->smbpid = req->smbpid;
262 f->pending_list = NULL;
264 f->share_access = io->generic.in.share_access;
265 f->impersonation = io->generic.in.impersonation;
266 f->access_mask = access_mask;
267 f->brl_handle = NULL;
268 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->sticky_write_time = False;
280 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
281 pvfs_directory_empty(pvfs, f->handle->name)) {
284 del_on_close = False;
288 /* form the lock context used for opendb locking */
289 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
290 if (!NT_STATUS_IS_OK(status)) {
294 /* get a lock on this file before the actual open */
295 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
297 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
299 /* we were supposed to do a blocking lock, so something
301 return NT_STATUS_INTERNAL_DB_CORRUPTION;
304 /* see if we are allowed to open at the same time as existing opens */
305 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
306 share_access, access_mask, del_on_close,
307 name->full_name, OPLOCK_NONE, NULL);
309 if (!NT_STATUS_IS_OK(status)) {
314 f->handle->have_opendb_entry = True;
317 DLIST_ADD(pvfs->files.list, f);
319 /* setup destructors to avoid leaks on abnormal termination */
320 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
321 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
324 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
325 mode_t mode = pvfs_fileperms(pvfs, attrib);
327 if (mkdir(name->full_name, mode) == -1) {
328 return pvfs_map_errno(pvfs,errno);
331 pvfs_xattr_unlink_hook(pvfs, name->full_name);
333 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
334 if (!NT_STATUS_IS_OK(status)) {
338 status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, f, io);
339 if (!NT_STATUS_IS_OK(status)) {
343 /* form the lock context used for opendb locking */
344 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
345 if (!NT_STATUS_IS_OK(status)) {
349 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
351 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
353 /* we were supposed to do a blocking lock, so something
355 return NT_STATUS_INTERNAL_DB_CORRUPTION;
358 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
359 share_access, access_mask, del_on_close,
360 name->full_name, OPLOCK_NONE, NULL);
362 if (!NT_STATUS_IS_OK(status)) {
366 f->handle->have_opendb_entry = True;
368 create_action = NTCREATEX_ACTION_CREATED;
370 notify_trigger(pvfs->notify_context,
372 FILE_NOTIFY_CHANGE_DIR_NAME,
375 create_action = NTCREATEX_ACTION_EXISTED;
379 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
382 /* the open succeeded, keep this handle permanently */
383 status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
384 if (!NT_STATUS_IS_OK(status)) {
388 io->generic.out.oplock_level = OPLOCK_NONE;
389 io->generic.out.file.ntvfs = h;
390 io->generic.out.create_action = create_action;
391 io->generic.out.create_time = name->dos.create_time;
392 io->generic.out.access_time = name->dos.access_time;
393 io->generic.out.write_time = name->dos.write_time;
394 io->generic.out.change_time = name->dos.change_time;
395 io->generic.out.attrib = name->dos.attrib;
396 io->generic.out.alloc_size = name->dos.alloc_size;
397 io->generic.out.size = name->st.st_size;
398 io->generic.out.file_type = FILE_TYPE_DISK;
399 io->generic.out.ipc_state = 0;
400 io->generic.out.is_directory = 1;
405 rmdir(name->full_name);
410 destroy a struct pvfs_file_handle
412 static int pvfs_handle_destructor(struct pvfs_file_handle *h)
417 /* the write time is no longer sticky */
418 if (h->sticky_write_time) {
420 status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
421 if (NT_STATUS_IS_OK(status)) {
422 h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
423 pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
427 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
428 h->name->stream_name) {
430 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
431 if (!NT_STATUS_IS_OK(status)) {
432 DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
433 h->name->stream_name, h->name->full_name));
438 if (close(h->fd) != 0) {
439 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
440 h->fd, h->name->full_name, strerror(errno)));
445 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->stream_id,
676 share_access, access_mask, del_on_close,
677 name->full_name, oplock_level, &oplock_granted);
679 if (!NT_STATUS_IS_OK(status)) {
680 /* bad news, we must have hit a race - we don't delete the file
681 here as the most likely scenario is that someone else created
682 the file at the same time */
687 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
688 oplock_granted = OPLOCK_BATCH;
692 f->session_info = req->session_info;
693 f->smbpid = req->smbpid;
695 f->pending_list = NULL;
697 f->share_access = io->generic.in.share_access;
698 f->access_mask = access_mask;
699 f->impersonation = io->generic.in.impersonation;
700 f->notify_buffer = NULL;
702 f->handle->pvfs = pvfs;
703 f->handle->name = talloc_steal(f->handle, name);
705 f->handle->create_options = io->generic.in.create_options;
706 f->handle->seek_offset = 0;
707 f->handle->position = 0;
709 f->handle->have_opendb_entry = True;
710 f->handle->sticky_write_time = False;
712 DLIST_ADD(pvfs->files.list, f);
714 /* setup a destructor to avoid file descriptor leaks on
715 abnormal termination */
716 talloc_set_destructor(f, pvfs_fnum_destructor);
717 talloc_set_destructor(f->handle, pvfs_handle_destructor);
719 io->generic.out.oplock_level = oplock_granted;
720 io->generic.out.file.ntvfs = f->ntvfs;
721 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
722 io->generic.out.create_time = name->dos.create_time;
723 io->generic.out.access_time = name->dos.access_time;
724 io->generic.out.write_time = name->dos.write_time;
725 io->generic.out.change_time = name->dos.change_time;
726 io->generic.out.attrib = name->dos.attrib;
727 io->generic.out.alloc_size = name->dos.alloc_size;
728 io->generic.out.size = name->st.st_size;
729 io->generic.out.file_type = FILE_TYPE_DISK;
730 io->generic.out.ipc_state = 0;
731 io->generic.out.is_directory = 0;
733 /* success - keep the file handle */
734 status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
735 if (!NT_STATUS_IS_OK(status)) {
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->session_info == req->session_info &&
863 f2->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;
993 BOOL stream_existed, stream_truncate=False;
994 uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
996 /* use the generic mapping code to avoid implementing all the
997 different open calls. */
998 if (io->generic.level != RAW_OPEN_GENERIC &&
999 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
1000 return ntvfs_map_open(ntvfs, req, io);
1003 /* resolve the cifs name to a posix name */
1004 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
1005 PVFS_RESOLVE_STREAMS, &name);
1006 if (!NT_STATUS_IS_OK(status)) {
1010 /* directory opens are handled separately */
1011 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
1012 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
1013 return pvfs_open_directory(pvfs, req, name, io);
1016 /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
1017 open doesn't match */
1018 io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
1020 create_options = io->generic.in.create_options;
1021 share_access = io->generic.in.share_access;
1022 access_mask = io->generic.in.access_mask;
1024 /* certain create options are not allowed */
1025 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
1026 !(access_mask & SEC_STD_DELETE)) {
1027 return NT_STATUS_INVALID_PARAMETER;
1032 switch (io->generic.in.open_disposition) {
1033 case NTCREATEX_DISP_SUPERSEDE:
1034 case NTCREATEX_DISP_OVERWRITE_IF:
1035 if (name->stream_name == NULL) {
1038 stream_truncate = True;
1042 case NTCREATEX_DISP_OPEN:
1043 if (!name->stream_exists) {
1044 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1049 case NTCREATEX_DISP_OVERWRITE:
1050 if (!name->stream_exists) {
1051 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1053 if (name->stream_name == NULL) {
1056 stream_truncate = True;
1060 case NTCREATEX_DISP_CREATE:
1061 if (name->stream_exists) {
1062 return NT_STATUS_OBJECT_NAME_COLLISION;
1067 case NTCREATEX_DISP_OPEN_IF:
1072 return NT_STATUS_INVALID_PARAMETER;
1075 /* handle creating a new file separately */
1076 if (!name->exists) {
1077 status = pvfs_create_file(pvfs, req, name, io);
1078 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1082 /* we've hit a race - the file was created during this call */
1083 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
1087 /* try re-resolving the name */
1088 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
1089 if (!NT_STATUS_IS_OK(status)) {
1092 /* fall through to a normal open */
1095 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1096 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1097 return NT_STATUS_CANNOT_DELETE;
1100 /* check the security descriptor */
1101 status = pvfs_access_check(pvfs, req, name, &access_mask);
1102 if (!NT_STATUS_IS_OK(status)) {
1106 status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
1107 NT_STATUS_NOT_OK_RETURN(status);
1109 f = talloc(h, struct pvfs_file);
1111 return NT_STATUS_NO_MEMORY;
1114 f->handle = talloc(f, struct pvfs_file_handle);
1115 if (f->handle == NULL) {
1116 return NT_STATUS_NO_MEMORY;
1120 f->session_info = req->session_info;
1121 f->smbpid = req->smbpid;
1123 f->pending_list = NULL;
1125 f->share_access = io->generic.in.share_access;
1126 f->access_mask = access_mask;
1127 f->impersonation = io->generic.in.impersonation;
1128 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;
1140 /* form the lock context used for byte range locking and
1142 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
1143 if (!NT_STATUS_IS_OK(status)) {
1147 status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle);
1148 if (!NT_STATUS_IS_OK(status)) {
1152 /* get a lock on this file before the actual open */
1153 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1155 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1157 /* we were supposed to do a blocking lock, so something
1159 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1162 DLIST_ADD(pvfs->files.list, f);
1164 /* setup a destructor to avoid file descriptor leaks on
1165 abnormal termination */
1166 talloc_set_destructor(f, pvfs_fnum_destructor);
1167 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1169 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1170 oplock_level = OPLOCK_NONE;
1171 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
1172 oplock_level = OPLOCK_BATCH;
1173 } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
1174 oplock_level = OPLOCK_EXCLUSIVE;
1177 /* see if we are allowed to open at the same time as existing opens */
1178 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
1179 share_access, access_mask, False, name->full_name,
1180 oplock_level, &oplock_granted);
1182 /* on a sharing violation we need to retry when the file is closed by
1183 the other user, or after 1 second */
1184 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1185 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1186 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1189 if (!NT_STATUS_IS_OK(status)) {
1194 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1195 oplock_granted = OPLOCK_BATCH;
1198 f->handle->have_opendb_entry = True;
1200 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1206 /* do the actual open */
1207 fd = open(f->handle->name->full_name, flags);
1210 return pvfs_map_errno(f->pvfs, errno);
1215 stream_existed = name->stream_exists;
1217 /* if this was a stream create then create the stream as well */
1218 if (!name->stream_exists) {
1219 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1220 if (!NT_STATUS_IS_OK(status)) {
1224 if (stream_truncate) {
1225 status = pvfs_stream_truncate(pvfs, f->handle->name, fd, 0);
1226 if (!NT_STATUS_IS_OK(status)) {
1233 /* re-resolve the open fd */
1234 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1235 if (!NT_STATUS_IS_OK(status)) {
1240 if (f->handle->name->stream_id == 0 &&
1241 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1242 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1243 /* for overwrite we need to replace file permissions */
1244 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1245 mode_t mode = pvfs_fileperms(pvfs, attrib);
1246 if (fchmod(fd, mode) == -1) {
1248 return pvfs_map_errno(pvfs, errno);
1250 name->dos.attrib = attrib;
1251 status = pvfs_dosattrib_save(pvfs, name, fd);
1252 if (!NT_STATUS_IS_OK(status)) {
1260 status = ntvfs_handle_set_backend_data(h, ntvfs, f);
1261 NT_STATUS_NOT_OK_RETURN(status);
1263 io->generic.out.oplock_level = oplock_granted;
1264 io->generic.out.file.ntvfs = h;
1265 io->generic.out.create_action = stream_existed?
1266 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1267 io->generic.out.create_time = name->dos.create_time;
1268 io->generic.out.access_time = name->dos.access_time;
1269 io->generic.out.write_time = name->dos.write_time;
1270 io->generic.out.change_time = name->dos.change_time;
1271 io->generic.out.attrib = name->dos.attrib;
1272 io->generic.out.alloc_size = name->dos.alloc_size;
1273 io->generic.out.size = name->st.st_size;
1274 io->generic.out.file_type = FILE_TYPE_DISK;
1275 io->generic.out.ipc_state = 0;
1276 io->generic.out.is_directory = 0;
1278 return NT_STATUS_OK;
1285 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1286 struct ntvfs_request *req, union smb_close *io)
1288 struct pvfs_state *pvfs = ntvfs->private_data;
1289 struct pvfs_file *f;
1290 struct utimbuf unix_times;
1292 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1293 return NT_STATUS_DOS(ERRSRV, ERRerror);
1296 if (io->generic.level != RAW_CLOSE_CLOSE) {
1297 return ntvfs_map_close(ntvfs, req, io);
1300 f = pvfs_find_fd(pvfs, req, io->close.in.file.ntvfs);
1302 return NT_STATUS_INVALID_HANDLE;
1305 if (!null_time(io->close.in.write_time)) {
1306 unix_times.actime = 0;
1307 unix_times.modtime = io->close.in.write_time;
1308 utime(f->handle->name->full_name, &unix_times);
1309 } else if (f->handle->sticky_write_time) {
1310 unix_times.actime = 0;
1311 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1312 utime(f->handle->name->full_name, &unix_times);
1317 return NT_STATUS_OK;
1322 logoff - close all file descriptors open by a vuid
1324 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1325 struct ntvfs_request *req)
1327 struct pvfs_state *pvfs = ntvfs->private_data;
1328 struct pvfs_file *f, *next;
1330 for (f=pvfs->files.list;f;f=next) {
1332 if (f->session_info == req->session_info) {
1337 return NT_STATUS_OK;
1342 exit - close files for the current pid
1344 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1345 struct ntvfs_request *req)
1347 struct pvfs_state *pvfs = ntvfs->private_data;
1348 struct pvfs_file *f, *next;
1350 for (f=pvfs->files.list;f;f=next) {
1352 if (f->session_info == req->session_info &&
1353 f->smbpid == req->smbpid) {
1358 return NT_STATUS_OK;
1363 change the delete on close flag on an already open file
1365 NTSTATUS pvfs_set_delete_on_close(struct pvfs_state *pvfs,
1366 struct ntvfs_request *req,
1367 struct pvfs_file *f, BOOL del_on_close)
1369 struct odb_lock *lck;
1372 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) && del_on_close) {
1373 return NT_STATUS_CANNOT_DELETE;
1376 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
1377 !pvfs_directory_empty(pvfs, f->handle->name)) {
1378 return NT_STATUS_DIRECTORY_NOT_EMPTY;
1382 f->handle->create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1384 f->handle->create_options &= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1387 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1389 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1392 status = odb_set_delete_on_close(lck, del_on_close);
1401 determine if a file can be deleted, or if it is prevented by an
1404 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
1405 struct ntvfs_request *req,
1406 struct pvfs_filename *name,
1407 struct odb_lock **lckp)
1411 struct odb_lock *lck;
1413 status = pvfs_locking_key(name, name, &key);
1414 if (!NT_STATUS_IS_OK(status)) {
1415 return NT_STATUS_NO_MEMORY;
1418 lck = odb_lock(req, pvfs->odb_context, &key);
1420 DEBUG(0,("Unable to lock opendb for can_delete\n"));
1421 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1424 status = odb_can_open(lck,
1425 NTCREATEX_SHARE_ACCESS_READ |
1426 NTCREATEX_SHARE_ACCESS_WRITE |
1427 NTCREATEX_SHARE_ACCESS_DELETE,
1428 NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
1431 if (NT_STATUS_IS_OK(status)) {
1432 status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
1435 if (!NT_STATUS_IS_OK(status)) {
1438 } else if (lckp != NULL) {
1446 determine if a file can be renamed, or if it is prevented by an
1449 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
1450 struct ntvfs_request *req,
1451 struct pvfs_filename *name,
1452 struct odb_lock **lckp)
1456 struct odb_lock *lck;
1458 status = pvfs_locking_key(name, name, &key);
1459 if (!NT_STATUS_IS_OK(status)) {
1460 return NT_STATUS_NO_MEMORY;
1463 lck = odb_lock(req, pvfs->odb_context, &key);
1465 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1466 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1469 status = odb_can_open(lck,
1470 NTCREATEX_SHARE_ACCESS_READ |
1471 NTCREATEX_SHARE_ACCESS_WRITE,
1475 if (!NT_STATUS_IS_OK(status)) {
1478 } else if (lckp != NULL) {
1486 determine if file meta data can be accessed, or if it is prevented by an
1489 NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
1490 struct ntvfs_request *req,
1491 struct pvfs_filename *name)
1495 struct odb_lock *lck;
1497 status = pvfs_locking_key(name, name, &key);
1498 if (!NT_STATUS_IS_OK(status)) {
1499 return NT_STATUS_NO_MEMORY;
1502 lck = odb_lock(req, pvfs->odb_context, &key);
1504 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1505 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1508 status = odb_can_open(lck,
1509 NTCREATEX_SHARE_ACCESS_READ |
1510 NTCREATEX_SHARE_ACCESS_WRITE,
1518 determine if delete on close is set on
1520 BOOL pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h,
1521 int *open_count, char **path)
1526 status = odb_get_delete_on_close(pvfs->odb_context, &h->odb_locking_key,
1527 &del_on_close, open_count, path);
1528 if (!NT_STATUS_IS_OK(status)) {
1529 DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
1533 return del_on_close;