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 create file handles with convenient numbers for sniffers
34 #define PVFS_MIN_FILE_FNUM 0x100
35 #define PVFS_MIN_NEW_FNUM 0x200
36 #define PVFS_MIN_DIR_FNUM 0x300
39 find open file handle given fnum
41 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
42 struct ntvfs_request *req, uint16_t fnum)
46 f = idr_find(pvfs->files.idtree, fnum);
51 if (f->fnum != fnum) {
52 smb_panic("pvfs_find_fd: idtree_fnum corruption\n");
55 if (req->session_info != f->session_info) {
56 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n",
66 cleanup a open directory handle
68 static int pvfs_dir_handle_destructor(void *p)
70 struct pvfs_file_handle *h = p;
74 if (h->name->stream_name == NULL &&
75 pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
78 status = pvfs_xattr_unlink_hook(h->pvfs, path);
79 if (!NT_STATUS_IS_OK(status)) {
80 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
81 path, nt_errstr(status)));
83 if (rmdir(path) != 0) {
84 DEBUG(0,("pvfs_dir_handle_destructor: failed to rmdir '%s' - %s\n",
85 path, strerror(errno)));
89 if (h->have_opendb_entry) {
93 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
95 DEBUG(0,("Unable to lock opendb for close\n"));
99 status = odb_close_file(lck, h);
100 if (!NT_STATUS_IS_OK(status)) {
101 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
102 h->name->full_name, nt_errstr(status)));
112 cleanup a open directory fnum
114 static int pvfs_dir_fnum_destructor(void *p)
116 struct pvfs_file *f = p;
117 DLIST_REMOVE(f->pvfs->files.list, f);
118 idr_remove(f->pvfs->files.idtree, f->fnum);
123 setup any EAs and the ACL on newly created files/directories
125 static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
126 struct ntvfs_request *req,
127 struct pvfs_filename *name,
133 /* setup any EAs that were asked for */
134 if (io->ntcreatex.in.ea_list) {
135 status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
136 io->ntcreatex.in.ea_list->num_eas,
137 io->ntcreatex.in.ea_list->eas);
138 if (!NT_STATUS_IS_OK(status)) {
143 /* setup an initial sec_desc if requested */
144 if (io->ntcreatex.in.sec_desc) {
145 union smb_setfileinfo set;
147 set.set_secdesc.in.file.fnum = fnum;
148 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
149 set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
151 status = pvfs_acl_set(pvfs, req, name, fd, SEC_STD_WRITE_DAC, &set);
153 /* otherwise setup an inherited acl from the parent */
154 status = pvfs_acl_inherit(pvfs, req, name, fd);
161 form the lock context used for opendb locking. Note that we must
162 zero here to take account of possible padding on some architectures
164 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
165 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
171 ZERO_STRUCT(lock_context);
173 lock_context.device = name->st.st_dev;
174 lock_context.inode = name->st.st_ino;
176 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
177 if (key->data == NULL) {
178 return NT_STATUS_NO_MEMORY;
188 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
189 struct ntvfs_request *req,
190 struct pvfs_filename *name,
196 uint32_t create_action;
197 uint32_t access_mask = io->generic.in.access_mask;
198 struct odb_lock *lck;
200 uint32_t create_options;
201 uint32_t share_access;
203 create_options = io->generic.in.create_options;
204 share_access = io->generic.in.share_access;
206 if (name->stream_name) {
207 return NT_STATUS_NOT_A_DIRECTORY;
210 /* if the client says it must be a directory, and it isn't,
212 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
213 return NT_STATUS_NOT_A_DIRECTORY;
216 switch (io->generic.in.open_disposition) {
217 case NTCREATEX_DISP_OPEN_IF:
220 case NTCREATEX_DISP_OPEN:
222 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
226 case NTCREATEX_DISP_CREATE:
228 return NT_STATUS_OBJECT_NAME_COLLISION;
232 case NTCREATEX_DISP_OVERWRITE_IF:
233 case NTCREATEX_DISP_OVERWRITE:
234 case NTCREATEX_DISP_SUPERSEDE:
236 return NT_STATUS_INVALID_PARAMETER;
239 f = talloc(req, struct pvfs_file);
241 return NT_STATUS_NO_MEMORY;
244 f->handle = talloc(f, struct pvfs_file_handle);
245 if (f->handle == NULL) {
246 return NT_STATUS_NO_MEMORY;
249 fnum = idr_get_new_above(pvfs->files.idtree, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
251 return NT_STATUS_TOO_MANY_OPENED_FILES;
255 /* check the security descriptor */
256 status = pvfs_access_check(pvfs, req, name, &access_mask);
258 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
260 if (!NT_STATUS_IS_OK(status)) {
261 idr_remove(pvfs->files.idtree, fnum);
266 f->session_info = req->session_info;
267 f->smbpid = req->smbpid;
269 f->pending_list = NULL;
271 f->share_access = io->generic.in.share_access;
272 f->impersonation = io->generic.in.impersonation;
273 f->access_mask = access_mask;
274 f->notify_buffer = NULL;
276 f->handle->pvfs = pvfs;
277 f->handle->name = talloc_steal(f->handle, name);
279 f->handle->odb_locking_key = data_blob(NULL, 0);
280 f->handle->brl_locking_key = data_blob(NULL, 0);
281 f->handle->create_options = io->generic.in.create_options;
282 f->handle->seek_offset = 0;
283 f->handle->position = 0;
285 f->handle->sticky_write_time = False;
287 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
288 pvfs_directory_empty(pvfs, f->handle->name)) {
291 del_on_close = False;
296 /* form the lock context used for opendb locking */
297 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
298 if (!NT_STATUS_IS_OK(status)) {
299 idr_remove(pvfs->files.idtree, f->fnum);
303 /* get a lock on this file before the actual open */
304 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
306 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
308 /* we were supposed to do a blocking lock, so something
310 idr_remove(pvfs->files.idtree, fnum);
311 return NT_STATUS_INTERNAL_DB_CORRUPTION;
314 /* see if we are allowed to open at the same time as existing opens */
315 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
316 share_access, access_mask, del_on_close, name->full_name);
318 if (!NT_STATUS_IS_OK(status)) {
319 idr_remove(pvfs->files.idtree, f->fnum);
324 f->handle->have_opendb_entry = True;
327 DLIST_ADD(pvfs->files.list, f);
329 /* setup destructors to avoid leaks on abnormal termination */
330 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
331 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
334 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
335 mode_t mode = pvfs_fileperms(pvfs, attrib);
337 if (mkdir(name->full_name, mode) == -1) {
338 idr_remove(pvfs->files.idtree, fnum);
339 return pvfs_map_errno(pvfs,errno);
342 pvfs_xattr_unlink_hook(pvfs, name->full_name);
344 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
345 if (!NT_STATUS_IS_OK(status)) {
349 status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, fnum, io);
350 if (!NT_STATUS_IS_OK(status)) {
354 /* form the lock context used for opendb locking */
355 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
356 if (!NT_STATUS_IS_OK(status)) {
357 idr_remove(pvfs->files.idtree, f->fnum);
361 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
363 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
365 /* we were supposed to do a blocking lock, so something
367 idr_remove(pvfs->files.idtree, fnum);
368 return NT_STATUS_INTERNAL_DB_CORRUPTION;
371 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
372 share_access, access_mask, del_on_close, name->full_name);
374 if (!NT_STATUS_IS_OK(status)) {
378 f->handle->have_opendb_entry = True;
380 create_action = NTCREATEX_ACTION_CREATED;
382 notify_trigger(pvfs->notify_context,
384 FILE_NOTIFY_CHANGE_DIR_NAME,
387 create_action = NTCREATEX_ACTION_EXISTED;
391 idr_remove(pvfs->files.idtree, fnum);
392 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
395 /* the open succeeded, keep this handle permanently */
396 talloc_steal(pvfs, f);
398 io->generic.out.oplock_level = OPLOCK_NONE;
399 io->generic.out.file.fnum = f->fnum;
400 io->generic.out.create_action = create_action;
401 io->generic.out.create_time = name->dos.create_time;
402 io->generic.out.access_time = name->dos.access_time;
403 io->generic.out.write_time = name->dos.write_time;
404 io->generic.out.change_time = name->dos.change_time;
405 io->generic.out.attrib = name->dos.attrib;
406 io->generic.out.alloc_size = name->dos.alloc_size;
407 io->generic.out.size = name->st.st_size;
408 io->generic.out.file_type = FILE_TYPE_DISK;
409 io->generic.out.ipc_state = 0;
410 io->generic.out.is_directory = 1;
415 idr_remove(pvfs->files.idtree, fnum);
416 rmdir(name->full_name);
421 destroy a struct pvfs_file_handle
423 static int pvfs_handle_destructor(void *p)
425 struct pvfs_file_handle *h = p;
429 /* the write time is no longer sticky */
430 if (h->sticky_write_time) {
432 status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
433 if (NT_STATUS_IS_OK(status)) {
434 h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
435 pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
439 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
440 h->name->stream_name) {
442 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
443 if (!NT_STATUS_IS_OK(status)) {
444 DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
445 h->name->stream_name, h->name->full_name));
450 if (close(h->fd) != 0) {
451 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
452 h->fd, h->name->full_name, strerror(errno)));
457 if (h->name->stream_name == NULL &&
458 pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
461 status = pvfs_xattr_unlink_hook(h->pvfs, path);
462 if (!NT_STATUS_IS_OK(status)) {
463 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
464 path, nt_errstr(status)));
466 if (unlink(path) != 0) {
467 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
468 path, strerror(errno)));
470 notify_trigger(h->pvfs->notify_context,
471 NOTIFY_ACTION_REMOVED,
472 FILE_NOTIFY_CHANGE_FILE_NAME,
477 if (h->have_opendb_entry) {
478 struct odb_lock *lck;
481 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
483 DEBUG(0,("Unable to lock opendb for close\n"));
487 status = odb_close_file(lck, h);
488 if (!NT_STATUS_IS_OK(status)) {
489 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
490 h->name->full_name, nt_errstr(status)));
501 destroy a struct pvfs_file
503 static int pvfs_fnum_destructor(void *p)
505 struct pvfs_file *f = p;
507 DLIST_REMOVE(f->pvfs->files.list, f);
508 pvfs_lock_close(f->pvfs, f);
509 idr_remove(f->pvfs->files.idtree, f->fnum);
516 form the lock context used for byte range locking. This is separate
517 from the locking key used for opendb locking as it needs to take
518 account of file streams (each stream is a separate byte range
521 static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name,
522 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
526 status = pvfs_locking_key(name, mem_ctx, &odb_key);
527 if (!NT_STATUS_IS_OK(status)) {
530 if (name->stream_name == NULL) {
534 *key = data_blob_talloc(mem_ctx, NULL,
535 odb_key.length + strlen(name->stream_name) + 1);
536 if (key->data == NULL) {
537 return NT_STATUS_NO_MEMORY;
539 memcpy(key->data, odb_key.data, odb_key.length);
540 memcpy(key->data + odb_key.length,
541 name->stream_name, strlen(name->stream_name)+1);
542 data_blob_free(&odb_key);
550 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
551 struct ntvfs_request *req,
552 struct pvfs_filename *name,
558 struct odb_lock *lck;
559 uint32_t create_options = io->generic.in.create_options;
560 uint32_t share_access = io->generic.in.share_access;
561 uint32_t access_mask = io->generic.in.access_mask;
565 struct pvfs_filename *parent;
567 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
568 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
569 return NT_STATUS_CANNOT_DELETE;
572 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
573 NT_STATUS_NOT_OK_RETURN(status);
575 /* check that the parent isn't opened with delete on close set */
576 status = pvfs_resolve_parent(pvfs, req, name, &parent);
577 if (NT_STATUS_IS_OK(status)) {
578 DATA_BLOB locking_key;
579 status = pvfs_locking_key(parent, req, &locking_key);
580 NT_STATUS_NOT_OK_RETURN(status);
581 status = odb_get_delete_on_close(pvfs->odb_context, &locking_key,
582 &del_on_close, NULL, NULL);
583 NT_STATUS_NOT_OK_RETURN(status);
585 return NT_STATUS_DELETE_PENDING;
589 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
595 f = talloc(req, struct pvfs_file);
597 return NT_STATUS_NO_MEMORY;
600 f->handle = talloc(f, struct pvfs_file_handle);
601 if (f->handle == NULL) {
602 return NT_STATUS_NO_MEMORY;
605 fnum = idr_get_new_above(pvfs->files.idtree, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
607 return NT_STATUS_TOO_MANY_OPENED_FILES;
610 attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
611 mode = pvfs_fileperms(pvfs, attrib);
613 /* create the file */
614 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
616 idr_remove(pvfs->files.idtree, fnum);
617 return pvfs_map_errno(pvfs, errno);
620 pvfs_xattr_unlink_hook(pvfs, name->full_name);
622 /* if this was a stream create then create the stream as well */
623 if (name->stream_name) {
624 status = pvfs_stream_create(pvfs, name, fd);
625 if (!NT_STATUS_IS_OK(status)) {
626 idr_remove(pvfs->files.idtree, fnum);
632 /* re-resolve the open fd */
633 status = pvfs_resolve_name_fd(pvfs, fd, name);
634 if (!NT_STATUS_IS_OK(status)) {
635 idr_remove(pvfs->files.idtree, fnum);
640 name->dos.attrib = attrib;
641 status = pvfs_dosattrib_save(pvfs, name, fd);
642 if (!NT_STATUS_IS_OK(status)) {
647 status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, fnum, io);
648 if (!NT_STATUS_IS_OK(status)) {
652 /* form the lock context used for byte range locking and
654 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
655 if (!NT_STATUS_IS_OK(status)) {
659 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
660 if (!NT_STATUS_IS_OK(status)) {
664 /* grab a lock on the open file record */
665 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
667 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
669 /* we were supposed to do a blocking lock, so something
671 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
675 if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
678 del_on_close = False;
681 status = odb_open_file(lck, f->handle, name->stream_id,
682 share_access, access_mask, del_on_close, name->full_name);
684 if (!NT_STATUS_IS_OK(status)) {
685 /* bad news, we must have hit a race - we don't delete the file
686 here as the most likely scenario is that someone else created
687 the file at the same time */
688 idr_remove(pvfs->files.idtree, fnum);
694 f->session_info = req->session_info;
695 f->smbpid = req->smbpid;
697 f->pending_list = NULL;
699 f->share_access = io->generic.in.share_access;
700 f->access_mask = access_mask;
701 f->impersonation = io->generic.in.impersonation;
702 f->notify_buffer = NULL;
704 f->handle->pvfs = pvfs;
705 f->handle->name = talloc_steal(f->handle, name);
707 f->handle->create_options = io->generic.in.create_options;
708 f->handle->seek_offset = 0;
709 f->handle->position = 0;
711 f->handle->have_opendb_entry = True;
712 f->handle->sticky_write_time = False;
714 DLIST_ADD(pvfs->files.list, f);
716 /* setup a destructor to avoid file descriptor leaks on
717 abnormal termination */
718 talloc_set_destructor(f, pvfs_fnum_destructor);
719 talloc_set_destructor(f->handle, pvfs_handle_destructor);
722 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
723 io->generic.out.oplock_level = OPLOCK_BATCH;
725 io->generic.out.oplock_level = OPLOCK_NONE;
727 io->generic.out.file.fnum = f->fnum;
728 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
729 io->generic.out.create_time = name->dos.create_time;
730 io->generic.out.access_time = name->dos.access_time;
731 io->generic.out.write_time = name->dos.write_time;
732 io->generic.out.change_time = name->dos.change_time;
733 io->generic.out.attrib = name->dos.attrib;
734 io->generic.out.alloc_size = name->dos.alloc_size;
735 io->generic.out.size = name->st.st_size;
736 io->generic.out.file_type = FILE_TYPE_DISK;
737 io->generic.out.ipc_state = 0;
738 io->generic.out.is_directory = 0;
740 /* success - keep the file handle */
741 talloc_steal(pvfs, f);
743 notify_trigger(pvfs->notify_context,
745 FILE_NOTIFY_CHANGE_FILE_NAME,
751 idr_remove(pvfs->files.idtree, fnum);
753 unlink(name->full_name);
759 state of a pending open retry
761 struct pvfs_open_retry {
762 struct ntvfs_module_context *ntvfs;
763 struct ntvfs_request *req;
766 DATA_BLOB odb_locking_key;
769 /* destroy a pending open request */
770 static int pvfs_retry_destructor(void *ptr)
772 struct pvfs_open_retry *r = ptr;
773 struct pvfs_state *pvfs = r->ntvfs->private_data;
774 if (r->odb_locking_key.data) {
775 struct odb_lock *lck;
776 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
778 odb_remove_pending(lck, r);
788 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
790 struct pvfs_open_retry *r = private;
791 struct ntvfs_module_context *ntvfs = r->ntvfs;
792 struct ntvfs_request *req = r->req;
793 union smb_open *io = r->io;
796 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
797 just a bug in their server, but we better do the same */
798 if (reason == PVFS_WAIT_CANCEL) {
802 talloc_free(r->wait_handle);
804 if (reason == PVFS_WAIT_TIMEOUT) {
805 /* if it timed out, then give the failure
808 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
809 req->async_states->send_fn(req);
813 /* the pending odb entry is already removed. We use a null locking
814 key to indicate this */
815 data_blob_free(&r->odb_locking_key);
818 /* try the open again, which could trigger another retry setup
819 if it wants to, so we have to unmark the async flag so we
820 will know if it does a second async reply */
821 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
823 status = pvfs_open(ntvfs, req, io);
824 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
825 /* the 2nd try also replied async, so we don't send
830 /* re-mark it async, just in case someone up the chain does
832 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
834 /* send the reply up the chain */
835 req->async_states->status = status;
836 req->async_states->send_fn(req);
841 special handling for openx DENY_DOS semantics
843 This function attempts a reference open using an existing handle. If its allowed,
844 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
845 open processing continues.
847 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
848 struct ntvfs_request *req, union smb_open *io,
849 struct pvfs_file *f, struct odb_lock *lck)
851 struct pvfs_state *pvfs = ntvfs->private_data;
852 struct pvfs_file *f2;
853 struct pvfs_filename *name;
855 /* search for an existing open with the right parameters. Note
856 the magic ntcreatex options flag, which is set in the
857 generic mapping code. This might look ugly, but its
858 actually pretty much now w2k does it internally as well.
860 If you look at the BASE-DENYDOS test you will see that a
861 DENY_DOS is a very special case, and in the right
862 circumstances you actually get the _same_ handle back
863 twice, rather than a new handle.
865 for (f2=pvfs->files.list;f2;f2=f2->next) {
867 f2->session_info == req->session_info &&
868 f2->smbpid == req->smbpid &&
869 (f2->handle->create_options &
870 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
871 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
872 (f2->access_mask & SEC_FILE_WRITE_DATA) &&
873 strcasecmp_m(f2->handle->name->original_name,
874 io->generic.in.fname)==0) {
880 return NT_STATUS_SHARING_VIOLATION;
883 /* quite an insane set of semantics ... */
884 if (is_exe_filename(io->generic.in.fname) &&
885 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
886 return NT_STATUS_SHARING_VIOLATION;
890 setup a reference to the existing handle
892 talloc_free(f->handle);
893 f->handle = talloc_reference(f, f2->handle);
897 name = f->handle->name;
899 io->generic.out.oplock_level = OPLOCK_NONE;
900 io->generic.out.file.fnum = f->fnum;
901 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
902 io->generic.out.create_time = name->dos.create_time;
903 io->generic.out.access_time = name->dos.access_time;
904 io->generic.out.write_time = name->dos.write_time;
905 io->generic.out.change_time = name->dos.change_time;
906 io->generic.out.attrib = name->dos.attrib;
907 io->generic.out.alloc_size = name->dos.alloc_size;
908 io->generic.out.size = name->st.st_size;
909 io->generic.out.file_type = FILE_TYPE_DISK;
910 io->generic.out.ipc_state = 0;
911 io->generic.out.is_directory = 0;
913 talloc_steal(f->pvfs, f);
921 setup for a open retry after a sharing violation
923 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
924 struct ntvfs_request *req,
927 struct odb_lock *lck)
929 struct pvfs_state *pvfs = ntvfs->private_data;
930 struct pvfs_open_retry *r;
932 struct timeval end_time;
934 if (io->generic.in.create_options &
935 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
936 /* see if we can satisfy the request using the special DENY_DOS
938 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
939 if (NT_STATUS_IS_OK(status)) {
944 r = talloc(req, struct pvfs_open_retry);
946 return NT_STATUS_NO_MEMORY;
952 r->odb_locking_key = data_blob_talloc(r,
953 f->handle->odb_locking_key.data,
954 f->handle->odb_locking_key.length);
956 end_time = timeval_add(&req->statistics.request_time, 0, pvfs->sharing_violation_delay);
958 /* setup a pending lock */
959 status = odb_open_file_pending(lck, r);
960 if (!NT_STATUS_IS_OK(status)) {
967 talloc_set_destructor(r, pvfs_retry_destructor);
969 r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
971 if (r->wait_handle == NULL) {
972 return NT_STATUS_NO_MEMORY;
975 talloc_steal(pvfs, r);
983 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
984 struct ntvfs_request *req, union smb_open *io)
986 struct pvfs_state *pvfs = ntvfs->private_data;
988 struct pvfs_filename *name;
992 struct odb_lock *lck;
993 uint32_t create_options;
994 uint32_t share_access;
995 uint32_t access_mask;
996 BOOL stream_existed, stream_truncate=False;
998 /* use the generic mapping code to avoid implementing all the
999 different open calls. */
1000 if (io->generic.level != RAW_OPEN_GENERIC &&
1001 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
1002 return ntvfs_map_open(ntvfs, req, io);
1005 /* resolve the cifs name to a posix name */
1006 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
1007 PVFS_RESOLVE_STREAMS, &name);
1008 if (!NT_STATUS_IS_OK(status)) {
1012 /* directory opens are handled separately */
1013 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
1014 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
1015 return pvfs_open_directory(pvfs, req, name, io);
1018 /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
1019 open doesn't match */
1020 io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
1022 create_options = io->generic.in.create_options;
1023 share_access = io->generic.in.share_access;
1024 access_mask = io->generic.in.access_mask;
1026 /* certain create options are not allowed */
1027 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
1028 !(access_mask & SEC_STD_DELETE)) {
1029 return NT_STATUS_INVALID_PARAMETER;
1034 switch (io->generic.in.open_disposition) {
1035 case NTCREATEX_DISP_SUPERSEDE:
1036 case NTCREATEX_DISP_OVERWRITE_IF:
1037 if (name->stream_name == NULL) {
1040 stream_truncate = True;
1044 case NTCREATEX_DISP_OPEN:
1045 if (!name->stream_exists) {
1046 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1051 case NTCREATEX_DISP_OVERWRITE:
1052 if (!name->stream_exists) {
1053 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1055 if (name->stream_name == NULL) {
1058 stream_truncate = True;
1062 case NTCREATEX_DISP_CREATE:
1063 if (name->stream_exists) {
1064 return NT_STATUS_OBJECT_NAME_COLLISION;
1069 case NTCREATEX_DISP_OPEN_IF:
1074 return NT_STATUS_INVALID_PARAMETER;
1077 /* handle creating a new file separately */
1078 if (!name->exists) {
1079 status = pvfs_create_file(pvfs, req, name, io);
1080 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1084 /* we've hit a race - the file was created during this call */
1085 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
1089 /* try re-resolving the name */
1090 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
1091 if (!NT_STATUS_IS_OK(status)) {
1094 /* fall through to a normal open */
1097 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1098 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1099 return NT_STATUS_CANNOT_DELETE;
1102 /* check the security descriptor */
1103 status = pvfs_access_check(pvfs, req, name, &access_mask);
1104 if (!NT_STATUS_IS_OK(status)) {
1108 f = talloc(req, struct pvfs_file);
1110 return NT_STATUS_NO_MEMORY;
1113 f->handle = talloc(f, struct pvfs_file_handle);
1114 if (f->handle == NULL) {
1115 return NT_STATUS_NO_MEMORY;
1118 /* allocate a fnum */
1119 fnum = idr_get_new_above(pvfs->files.idtree, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
1121 return NT_STATUS_TOO_MANY_OPENED_FILES;
1125 f->session_info = req->session_info;
1126 f->smbpid = req->smbpid;
1128 f->pending_list = NULL;
1130 f->share_access = io->generic.in.share_access;
1131 f->access_mask = access_mask;
1132 f->impersonation = io->generic.in.impersonation;
1133 f->notify_buffer = NULL;
1135 f->handle->pvfs = pvfs;
1137 f->handle->name = talloc_steal(f->handle, name);
1138 f->handle->create_options = io->generic.in.create_options;
1139 f->handle->seek_offset = 0;
1140 f->handle->position = 0;
1141 f->handle->mode = 0;
1142 f->handle->have_opendb_entry = False;
1143 f->handle->sticky_write_time = False;
1145 /* form the lock context used for byte range locking and
1147 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
1148 if (!NT_STATUS_IS_OK(status)) {
1149 idr_remove(pvfs->files.idtree, f->fnum);
1153 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
1154 if (!NT_STATUS_IS_OK(status)) {
1155 idr_remove(pvfs->files.idtree, f->fnum);
1159 /* get a lock on this file before the actual open */
1160 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1162 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1164 /* we were supposed to do a blocking lock, so something
1166 idr_remove(pvfs->files.idtree, fnum);
1167 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1170 DLIST_ADD(pvfs->files.list, f);
1172 /* setup a destructor to avoid file descriptor leaks on
1173 abnormal termination */
1174 talloc_set_destructor(f, pvfs_fnum_destructor);
1175 talloc_set_destructor(f->handle, pvfs_handle_destructor);
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);
1181 /* on a sharing violation we need to retry when the file is closed by
1182 the other user, or after 1 second */
1183 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1184 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1185 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1188 if (!NT_STATUS_IS_OK(status)) {
1193 f->handle->have_opendb_entry = True;
1195 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1201 /* do the actual open */
1202 fd = open(f->handle->name->full_name, flags);
1205 return pvfs_map_errno(f->pvfs, errno);
1210 stream_existed = name->stream_exists;
1212 /* if this was a stream create then create the stream as well */
1213 if (!name->stream_exists) {
1214 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1215 if (!NT_STATUS_IS_OK(status)) {
1219 if (stream_truncate) {
1220 status = pvfs_stream_truncate(pvfs, f->handle->name, fd, 0);
1221 if (!NT_STATUS_IS_OK(status)) {
1228 /* re-resolve the open fd */
1229 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1230 if (!NT_STATUS_IS_OK(status)) {
1235 if (f->handle->name->stream_id == 0 &&
1236 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1237 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1238 /* for overwrite we need to replace file permissions */
1239 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1240 mode_t mode = pvfs_fileperms(pvfs, attrib);
1241 if (fchmod(fd, mode) == -1) {
1243 return pvfs_map_errno(pvfs, errno);
1245 name->dos.attrib = attrib;
1246 status = pvfs_dosattrib_save(pvfs, name, fd);
1247 if (!NT_STATUS_IS_OK(status)) {
1255 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1256 io->generic.out.oplock_level = OPLOCK_BATCH;
1258 io->generic.out.oplock_level = OPLOCK_NONE;
1260 io->generic.out.file.fnum = f->fnum;
1261 io->generic.out.create_action = stream_existed?
1262 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1263 io->generic.out.create_time = name->dos.create_time;
1264 io->generic.out.access_time = name->dos.access_time;
1265 io->generic.out.write_time = name->dos.write_time;
1266 io->generic.out.change_time = name->dos.change_time;
1267 io->generic.out.attrib = name->dos.attrib;
1268 io->generic.out.alloc_size = name->dos.alloc_size;
1269 io->generic.out.size = name->st.st_size;
1270 io->generic.out.file_type = FILE_TYPE_DISK;
1271 io->generic.out.ipc_state = 0;
1272 io->generic.out.is_directory = 0;
1274 /* success - keep the file handle */
1275 talloc_steal(f->pvfs, f);
1277 return NT_STATUS_OK;
1284 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1285 struct ntvfs_request *req, union smb_close *io)
1287 struct pvfs_state *pvfs = ntvfs->private_data;
1288 struct pvfs_file *f;
1289 struct utimbuf unix_times;
1291 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1292 return NT_STATUS_DOS(ERRSRV, ERRerror);
1295 if (io->generic.level != RAW_CLOSE_CLOSE) {
1296 return ntvfs_map_close(ntvfs, req, io);
1299 f = pvfs_find_fd(pvfs, req, io->close.in.file.fnum);
1301 return NT_STATUS_INVALID_HANDLE;
1304 if (!null_time(io->close.in.write_time)) {
1305 unix_times.actime = 0;
1306 unix_times.modtime = io->close.in.write_time;
1307 utime(f->handle->name->full_name, &unix_times);
1308 } else if (f->handle->sticky_write_time) {
1309 unix_times.actime = 0;
1310 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1311 utime(f->handle->name->full_name, &unix_times);
1316 return NT_STATUS_OK;
1321 logoff - close all file descriptors open by a vuid
1323 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1324 struct ntvfs_request *req)
1326 struct pvfs_state *pvfs = ntvfs->private_data;
1327 struct pvfs_file *f, *next;
1329 for (f=pvfs->files.list;f;f=next) {
1331 if (f->session_info == req->session_info) {
1336 return NT_STATUS_OK;
1341 exit - close files for the current pid
1343 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1344 struct ntvfs_request *req)
1346 struct pvfs_state *pvfs = ntvfs->private_data;
1347 struct pvfs_file *f, *next;
1349 for (f=pvfs->files.list;f;f=next) {
1351 if (f->session_info == req->session_info &&
1352 f->smbpid == req->smbpid) {
1357 return NT_STATUS_OK;
1362 change the delete on close flag on an already open file
1364 NTSTATUS pvfs_set_delete_on_close(struct pvfs_state *pvfs,
1365 struct ntvfs_request *req,
1366 struct pvfs_file *f, BOOL del_on_close)
1368 struct odb_lock *lck;
1371 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) && del_on_close) {
1372 return NT_STATUS_CANNOT_DELETE;
1375 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
1376 !pvfs_directory_empty(pvfs, f->handle->name)) {
1377 return NT_STATUS_DIRECTORY_NOT_EMPTY;
1381 f->handle->create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1383 f->handle->create_options &= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1386 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1388 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1391 status = odb_set_delete_on_close(lck, del_on_close);
1400 determine if a file can be deleted, or if it is prevented by an
1403 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
1404 struct ntvfs_request *req,
1405 struct pvfs_filename *name,
1406 struct odb_lock **lckp)
1410 struct odb_lock *lck;
1412 status = pvfs_locking_key(name, name, &key);
1413 if (!NT_STATUS_IS_OK(status)) {
1414 return NT_STATUS_NO_MEMORY;
1417 lck = odb_lock(req, pvfs->odb_context, &key);
1419 DEBUG(0,("Unable to lock opendb for can_delete\n"));
1420 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1423 status = odb_can_open(lck,
1424 NTCREATEX_SHARE_ACCESS_READ |
1425 NTCREATEX_SHARE_ACCESS_WRITE |
1426 NTCREATEX_SHARE_ACCESS_DELETE,
1427 NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
1430 if (NT_STATUS_IS_OK(status)) {
1431 status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
1434 if (!NT_STATUS_IS_OK(status)) {
1437 } else if (lckp != NULL) {
1445 determine if a file can be renamed, or if it is prevented by an
1448 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
1449 struct ntvfs_request *req,
1450 struct pvfs_filename *name,
1451 struct odb_lock **lckp)
1455 struct odb_lock *lck;
1457 status = pvfs_locking_key(name, name, &key);
1458 if (!NT_STATUS_IS_OK(status)) {
1459 return NT_STATUS_NO_MEMORY;
1462 lck = odb_lock(req, pvfs->odb_context, &key);
1464 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1465 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1468 status = odb_can_open(lck,
1469 NTCREATEX_SHARE_ACCESS_READ |
1470 NTCREATEX_SHARE_ACCESS_WRITE,
1474 if (!NT_STATUS_IS_OK(status)) {
1477 } else if (lckp != NULL) {
1485 determine if file meta data can be accessed, or if it is prevented by an
1488 NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
1489 struct ntvfs_request *req,
1490 struct pvfs_filename *name)
1494 struct odb_lock *lck;
1496 status = pvfs_locking_key(name, name, &key);
1497 if (!NT_STATUS_IS_OK(status)) {
1498 return NT_STATUS_NO_MEMORY;
1501 lck = odb_lock(req, pvfs->odb_context, &key);
1503 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1504 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1507 status = odb_can_open(lck,
1508 NTCREATEX_SHARE_ACCESS_READ |
1509 NTCREATEX_SHARE_ACCESS_WRITE,
1517 determine if delete on close is set on
1519 BOOL pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h,
1520 int *open_count, char **path)
1525 status = odb_get_delete_on_close(pvfs->odb_context, &h->odb_locking_key,
1526 &del_on_close, open_count, path);
1527 if (!NT_STATUS_IS_OK(status)) {
1528 DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
1532 return del_on_close;