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/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->idtree_fnum, fnum);
51 if (f->fnum != fnum) {
52 smb_panic("pvfs_find_fd: idtree_fnum corruption\n");
55 if (req->session != f->session) {
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->open_files, f);
118 idr_remove(f->pvfs->idtree_fnum, 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.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->idtree_fnum, 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->idtree_fnum, fnum);
266 f->session = req->session;
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;
275 f->handle->pvfs = pvfs;
276 f->handle->name = talloc_steal(f->handle, name);
278 f->handle->odb_locking_key = data_blob(NULL, 0);
279 f->handle->brl_locking_key = data_blob(NULL, 0);
280 f->handle->create_options = io->generic.in.create_options;
281 f->handle->seek_offset = 0;
282 f->handle->position = 0;
284 f->handle->sticky_write_time = False;
286 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
287 pvfs_directory_empty(pvfs, f->handle->name)) {
290 del_on_close = False;
295 /* form the lock context used for opendb locking */
296 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
297 if (!NT_STATUS_IS_OK(status)) {
298 idr_remove(pvfs->idtree_fnum, f->fnum);
302 /* get a lock on this file before the actual open */
303 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
305 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
307 /* we were supposed to do a blocking lock, so something
309 idr_remove(pvfs->idtree_fnum, fnum);
310 return NT_STATUS_INTERNAL_DB_CORRUPTION;
313 /* see if we are allowed to open at the same time as existing opens */
314 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
315 share_access, access_mask, del_on_close, name->full_name);
317 if (!NT_STATUS_IS_OK(status)) {
318 idr_remove(pvfs->idtree_fnum, f->fnum);
323 f->handle->have_opendb_entry = True;
326 DLIST_ADD(pvfs->open_files, f);
328 /* setup destructors to avoid leaks on abnormal termination */
329 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
330 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
333 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
334 mode_t mode = pvfs_fileperms(pvfs, attrib);
336 if (mkdir(name->full_name, mode) == -1) {
337 idr_remove(pvfs->idtree_fnum, fnum);
338 return pvfs_map_errno(pvfs,errno);
341 pvfs_xattr_unlink_hook(pvfs, name->full_name);
343 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
344 if (!NT_STATUS_IS_OK(status)) {
348 status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, fnum, io);
349 if (!NT_STATUS_IS_OK(status)) {
353 /* form the lock context used for opendb locking */
354 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
355 if (!NT_STATUS_IS_OK(status)) {
356 idr_remove(pvfs->idtree_fnum, f->fnum);
360 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
362 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
364 /* we were supposed to do a blocking lock, so something
366 idr_remove(pvfs->idtree_fnum, fnum);
367 return NT_STATUS_INTERNAL_DB_CORRUPTION;
370 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
371 share_access, access_mask, del_on_close, name->full_name);
373 if (!NT_STATUS_IS_OK(status)) {
377 f->handle->have_opendb_entry = True;
379 create_action = NTCREATEX_ACTION_CREATED;
381 create_action = NTCREATEX_ACTION_EXISTED;
385 idr_remove(pvfs->idtree_fnum, fnum);
386 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
389 /* the open succeeded, keep this handle permanently */
390 talloc_steal(pvfs, f);
392 io->generic.out.oplock_level = OPLOCK_NONE;
393 io->generic.out.fnum = f->fnum;
394 io->generic.out.create_action = create_action;
395 io->generic.out.create_time = name->dos.create_time;
396 io->generic.out.access_time = name->dos.access_time;
397 io->generic.out.write_time = name->dos.write_time;
398 io->generic.out.change_time = name->dos.change_time;
399 io->generic.out.attrib = name->dos.attrib;
400 io->generic.out.alloc_size = name->dos.alloc_size;
401 io->generic.out.size = name->st.st_size;
402 io->generic.out.file_type = FILE_TYPE_DISK;
403 io->generic.out.ipc_state = 0;
404 io->generic.out.is_directory = 1;
409 idr_remove(pvfs->idtree_fnum, fnum);
410 rmdir(name->full_name);
415 destroy a struct pvfs_file_handle
417 static int pvfs_handle_destructor(void *p)
419 struct pvfs_file_handle *h = p;
423 /* the write time is no longer sticky */
424 if (h->sticky_write_time) {
426 status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
427 if (NT_STATUS_IS_OK(status)) {
428 h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
429 pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
433 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
434 h->name->stream_name) {
436 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
437 if (!NT_STATUS_IS_OK(status)) {
438 DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
439 h->name->stream_name, h->name->full_name));
444 if (close(h->fd) != 0) {
445 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
446 h->fd, h->name->full_name, strerror(errno)));
451 if (h->name->stream_name == NULL &&
452 pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
455 status = pvfs_xattr_unlink_hook(h->pvfs, path);
456 if (!NT_STATUS_IS_OK(status)) {
457 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
458 path, nt_errstr(status)));
460 if (unlink(path) != 0) {
461 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
462 path, strerror(errno)));
466 if (h->have_opendb_entry) {
467 struct odb_lock *lck;
470 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
472 DEBUG(0,("Unable to lock opendb for close\n"));
476 status = odb_close_file(lck, h);
477 if (!NT_STATUS_IS_OK(status)) {
478 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
479 h->name->full_name, nt_errstr(status)));
490 destroy a struct pvfs_file
492 static int pvfs_fnum_destructor(void *p)
494 struct pvfs_file *f = p;
496 DLIST_REMOVE(f->pvfs->open_files, f);
497 pvfs_lock_close(f->pvfs, f);
498 idr_remove(f->pvfs->idtree_fnum, f->fnum);
505 form the lock context used for byte range locking. This is separate
506 from the locking key used for opendb locking as it needs to take
507 account of file streams (each stream is a separate byte range
510 static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name,
511 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
515 status = pvfs_locking_key(name, mem_ctx, &odb_key);
516 if (!NT_STATUS_IS_OK(status)) {
519 if (name->stream_name == NULL) {
523 *key = data_blob_talloc(mem_ctx, NULL,
524 odb_key.length + strlen(name->stream_name) + 1);
525 if (key->data == NULL) {
526 return NT_STATUS_NO_MEMORY;
528 memcpy(key->data, odb_key.data, odb_key.length);
529 memcpy(key->data + odb_key.length,
530 name->stream_name, strlen(name->stream_name)+1);
531 data_blob_free(&odb_key);
539 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
540 struct ntvfs_request *req,
541 struct pvfs_filename *name,
547 struct odb_lock *lck;
548 uint32_t create_options = io->generic.in.create_options;
549 uint32_t share_access = io->generic.in.share_access;
550 uint32_t access_mask = io->generic.in.access_mask;
554 struct pvfs_filename *parent;
556 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
557 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
558 return NT_STATUS_CANNOT_DELETE;
561 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
562 NT_STATUS_NOT_OK_RETURN(status);
564 /* check that the parent isn't opened with delete on close set */
565 status = pvfs_resolve_parent(pvfs, req, name, &parent);
566 if (NT_STATUS_IS_OK(status)) {
567 DATA_BLOB locking_key;
568 status = pvfs_locking_key(parent, req, &locking_key);
569 NT_STATUS_NOT_OK_RETURN(status);
570 status = odb_get_delete_on_close(pvfs->odb_context, &locking_key,
571 &del_on_close, NULL, NULL);
572 NT_STATUS_NOT_OK_RETURN(status);
574 return NT_STATUS_DELETE_PENDING;
578 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
584 f = talloc(req, struct pvfs_file);
586 return NT_STATUS_NO_MEMORY;
589 f->handle = talloc(f, struct pvfs_file_handle);
590 if (f->handle == NULL) {
591 return NT_STATUS_NO_MEMORY;
594 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
596 return NT_STATUS_TOO_MANY_OPENED_FILES;
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 idr_remove(pvfs->idtree_fnum, fnum);
606 return pvfs_map_errno(pvfs, errno);
609 pvfs_xattr_unlink_hook(pvfs, name->full_name);
611 /* if this was a stream create then create the stream as well */
612 if (name->stream_name) {
613 status = pvfs_stream_create(pvfs, name, fd);
614 if (!NT_STATUS_IS_OK(status)) {
615 idr_remove(pvfs->idtree_fnum, fnum);
621 /* re-resolve the open fd */
622 status = pvfs_resolve_name_fd(pvfs, fd, name);
623 if (!NT_STATUS_IS_OK(status)) {
624 idr_remove(pvfs->idtree_fnum, fnum);
629 name->dos.attrib = attrib;
630 status = pvfs_dosattrib_save(pvfs, name, fd);
631 if (!NT_STATUS_IS_OK(status)) {
636 status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, fnum, io);
637 if (!NT_STATUS_IS_OK(status)) {
641 /* form the lock context used for byte range locking and
643 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
644 if (!NT_STATUS_IS_OK(status)) {
648 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
649 if (!NT_STATUS_IS_OK(status)) {
653 /* grab a lock on the open file record */
654 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
656 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
658 /* we were supposed to do a blocking lock, so something
660 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
664 if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
667 del_on_close = False;
670 status = odb_open_file(lck, f->handle, name->stream_id,
671 share_access, access_mask, del_on_close, name->full_name);
673 if (!NT_STATUS_IS_OK(status)) {
674 /* bad news, we must have hit a race - we don't delete the file
675 here as the most likely scenario is that someone else created
676 the file at the same time */
677 idr_remove(pvfs->idtree_fnum, fnum);
683 f->session = req->session;
684 f->smbpid = req->smbpid;
686 f->pending_list = NULL;
688 f->share_access = io->generic.in.share_access;
689 f->access_mask = access_mask;
690 f->impersonation = io->generic.in.impersonation;
692 f->handle->pvfs = pvfs;
693 f->handle->name = talloc_steal(f->handle, name);
695 f->handle->create_options = io->generic.in.create_options;
696 f->handle->seek_offset = 0;
697 f->handle->position = 0;
699 f->handle->have_opendb_entry = True;
700 f->handle->sticky_write_time = False;
702 DLIST_ADD(pvfs->open_files, f);
704 /* setup a destructor to avoid file descriptor leaks on
705 abnormal termination */
706 talloc_set_destructor(f, pvfs_fnum_destructor);
707 talloc_set_destructor(f->handle, pvfs_handle_destructor);
710 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
711 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
713 io->generic.out.oplock_level = OPLOCK_NONE;
715 io->generic.out.fnum = f->fnum;
716 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
717 io->generic.out.create_time = name->dos.create_time;
718 io->generic.out.access_time = name->dos.access_time;
719 io->generic.out.write_time = name->dos.write_time;
720 io->generic.out.change_time = name->dos.change_time;
721 io->generic.out.attrib = name->dos.attrib;
722 io->generic.out.alloc_size = name->dos.alloc_size;
723 io->generic.out.size = name->st.st_size;
724 io->generic.out.file_type = FILE_TYPE_DISK;
725 io->generic.out.ipc_state = 0;
726 io->generic.out.is_directory = 0;
728 /* success - keep the file handle */
729 talloc_steal(pvfs, f);
734 idr_remove(pvfs->idtree_fnum, fnum);
736 unlink(name->full_name);
742 state of a pending open retry
744 struct pvfs_open_retry {
745 struct ntvfs_module_context *ntvfs;
746 struct ntvfs_request *req;
749 DATA_BLOB odb_locking_key;
752 /* destroy a pending open request */
753 static int pvfs_retry_destructor(void *ptr)
755 struct pvfs_open_retry *r = ptr;
756 struct pvfs_state *pvfs = r->ntvfs->private_data;
757 if (r->odb_locking_key.data) {
758 struct odb_lock *lck;
759 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
761 odb_remove_pending(lck, r);
771 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
773 struct pvfs_open_retry *r = private;
774 struct ntvfs_module_context *ntvfs = r->ntvfs;
775 struct ntvfs_request *req = r->req;
776 union smb_open *io = r->io;
779 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
780 just a bug in their server, but we better do the same */
781 if (reason == PVFS_WAIT_CANCEL) {
785 talloc_free(r->wait_handle);
787 if (reason == PVFS_WAIT_TIMEOUT) {
788 /* if it timed out, then give the failure
791 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
792 req->async_states->send_fn(req);
796 /* the pending odb entry is already removed. We use a null locking
797 key to indicate this */
798 data_blob_free(&r->odb_locking_key);
801 /* try the open again, which could trigger another retry setup
802 if it wants to, so we have to unmark the async flag so we
803 will know if it does a second async reply */
804 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
806 status = pvfs_open(ntvfs, req, io);
807 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
808 /* the 2nd try also replied async, so we don't send
813 /* re-mark it async, just in case someone up the chain does
815 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
817 /* send the reply up the chain */
818 req->async_states->status = status;
819 req->async_states->send_fn(req);
824 special handling for openx DENY_DOS semantics
826 This function attempts a reference open using an existing handle. If its allowed,
827 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
828 open processing continues.
830 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
831 struct ntvfs_request *req, union smb_open *io,
832 struct pvfs_file *f, struct odb_lock *lck)
834 struct pvfs_state *pvfs = ntvfs->private_data;
835 struct pvfs_file *f2;
836 struct pvfs_filename *name;
838 /* search for an existing open with the right parameters. Note
839 the magic ntcreatex options flag, which is set in the
840 generic mapping code. This might look ugly, but its
841 actually pretty much now w2k does it internally as well.
843 If you look at the BASE-DENYDOS test you will see that a
844 DENY_DOS is a very special case, and in the right
845 circumstances you actually get the _same_ handle back
846 twice, rather than a new handle.
848 for (f2=pvfs->open_files;f2;f2=f2->next) {
850 f2->session == req->session &&
851 f2->smbpid == req->smbpid &&
852 (f2->handle->create_options &
853 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
854 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
855 (f2->access_mask & SEC_FILE_WRITE_DATA) &&
856 strcasecmp_m(f2->handle->name->original_name,
857 io->generic.in.fname)==0) {
863 return NT_STATUS_SHARING_VIOLATION;
866 /* quite an insane set of semantics ... */
867 if (is_exe_filename(io->generic.in.fname) &&
868 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
869 return NT_STATUS_SHARING_VIOLATION;
873 setup a reference to the existing handle
875 talloc_free(f->handle);
876 f->handle = talloc_reference(f, f2->handle);
880 name = f->handle->name;
882 io->generic.out.oplock_level = OPLOCK_NONE;
883 io->generic.out.fnum = f->fnum;
884 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
885 io->generic.out.create_time = name->dos.create_time;
886 io->generic.out.access_time = name->dos.access_time;
887 io->generic.out.write_time = name->dos.write_time;
888 io->generic.out.change_time = name->dos.change_time;
889 io->generic.out.attrib = name->dos.attrib;
890 io->generic.out.alloc_size = name->dos.alloc_size;
891 io->generic.out.size = name->st.st_size;
892 io->generic.out.file_type = FILE_TYPE_DISK;
893 io->generic.out.ipc_state = 0;
894 io->generic.out.is_directory = 0;
896 talloc_steal(f->pvfs, f);
904 setup for a open retry after a sharing violation
906 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
907 struct ntvfs_request *req,
910 struct odb_lock *lck)
912 struct pvfs_state *pvfs = ntvfs->private_data;
913 struct pvfs_open_retry *r;
915 struct timeval end_time;
917 if (io->generic.in.create_options &
918 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
919 /* see if we can satisfy the request using the special DENY_DOS
921 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
922 if (NT_STATUS_IS_OK(status)) {
927 r = talloc(req, struct pvfs_open_retry);
929 return NT_STATUS_NO_MEMORY;
935 r->odb_locking_key = data_blob_talloc(r,
936 f->handle->odb_locking_key.data,
937 f->handle->odb_locking_key.length);
939 end_time = timeval_add(&req->request_time, 0, pvfs->sharing_violation_delay);
941 /* setup a pending lock */
942 status = odb_open_file_pending(lck, r);
943 if (!NT_STATUS_IS_OK(status)) {
950 talloc_set_destructor(r, pvfs_retry_destructor);
952 r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
954 if (r->wait_handle == NULL) {
955 return NT_STATUS_NO_MEMORY;
958 talloc_steal(pvfs, r);
966 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
967 struct ntvfs_request *req, union smb_open *io)
969 struct pvfs_state *pvfs = ntvfs->private_data;
971 struct pvfs_filename *name;
975 struct odb_lock *lck;
976 uint32_t create_options;
977 uint32_t share_access;
978 uint32_t access_mask;
979 BOOL stream_existed, stream_truncate=False;
981 /* use the generic mapping code to avoid implementing all the
982 different open calls. */
983 if (io->generic.level != RAW_OPEN_GENERIC &&
984 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
985 return ntvfs_map_open(ntvfs, req, io);
988 /* resolve the cifs name to a posix name */
989 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
990 PVFS_RESOLVE_STREAMS, &name);
991 if (!NT_STATUS_IS_OK(status)) {
995 /* directory opens are handled separately */
996 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
997 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
998 return pvfs_open_directory(pvfs, req, name, io);
1001 /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
1002 open doesn't match */
1003 io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
1005 create_options = io->generic.in.create_options;
1006 share_access = io->generic.in.share_access;
1007 access_mask = io->generic.in.access_mask;
1009 /* certain create options are not allowed */
1010 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
1011 !(access_mask & SEC_STD_DELETE)) {
1012 return NT_STATUS_INVALID_PARAMETER;
1017 switch (io->generic.in.open_disposition) {
1018 case NTCREATEX_DISP_SUPERSEDE:
1019 case NTCREATEX_DISP_OVERWRITE_IF:
1020 if (name->stream_name == NULL) {
1023 stream_truncate = True;
1027 case NTCREATEX_DISP_OPEN:
1028 if (!name->stream_exists) {
1029 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1034 case NTCREATEX_DISP_OVERWRITE:
1035 if (!name->stream_exists) {
1036 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1038 if (name->stream_name == NULL) {
1041 stream_truncate = True;
1045 case NTCREATEX_DISP_CREATE:
1046 if (name->stream_exists) {
1047 return NT_STATUS_OBJECT_NAME_COLLISION;
1052 case NTCREATEX_DISP_OPEN_IF:
1057 return NT_STATUS_INVALID_PARAMETER;
1060 /* handle creating a new file separately */
1061 if (!name->exists) {
1062 status = pvfs_create_file(pvfs, req, name, io);
1063 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1067 /* we've hit a race - the file was created during this call */
1068 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
1072 /* try re-resolving the name */
1073 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
1074 if (!NT_STATUS_IS_OK(status)) {
1077 /* fall through to a normal open */
1080 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1081 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1082 return NT_STATUS_CANNOT_DELETE;
1085 /* check the security descriptor */
1086 status = pvfs_access_check(pvfs, req, name, &access_mask);
1087 if (!NT_STATUS_IS_OK(status)) {
1091 f = talloc(req, struct pvfs_file);
1093 return NT_STATUS_NO_MEMORY;
1096 f->handle = talloc(f, struct pvfs_file_handle);
1097 if (f->handle == NULL) {
1098 return NT_STATUS_NO_MEMORY;
1101 /* allocate a fnum */
1102 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
1104 return NT_STATUS_TOO_MANY_OPENED_FILES;
1108 f->session = req->session;
1109 f->smbpid = req->smbpid;
1111 f->pending_list = NULL;
1113 f->share_access = io->generic.in.share_access;
1114 f->access_mask = access_mask;
1115 f->impersonation = io->generic.in.impersonation;
1117 f->handle->pvfs = pvfs;
1119 f->handle->name = talloc_steal(f->handle, name);
1120 f->handle->create_options = io->generic.in.create_options;
1121 f->handle->seek_offset = 0;
1122 f->handle->position = 0;
1123 f->handle->mode = 0;
1124 f->handle->have_opendb_entry = False;
1125 f->handle->sticky_write_time = False;
1127 /* form the lock context used for byte range locking and
1129 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
1130 if (!NT_STATUS_IS_OK(status)) {
1131 idr_remove(pvfs->idtree_fnum, f->fnum);
1135 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
1136 if (!NT_STATUS_IS_OK(status)) {
1137 idr_remove(pvfs->idtree_fnum, f->fnum);
1141 /* get a lock on this file before the actual open */
1142 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1144 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1146 /* we were supposed to do a blocking lock, so something
1148 idr_remove(pvfs->idtree_fnum, fnum);
1149 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1152 DLIST_ADD(pvfs->open_files, f);
1154 /* setup a destructor to avoid file descriptor leaks on
1155 abnormal termination */
1156 talloc_set_destructor(f, pvfs_fnum_destructor);
1157 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1159 /* see if we are allowed to open at the same time as existing opens */
1160 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
1161 share_access, access_mask, False, name->full_name);
1163 /* on a sharing violation we need to retry when the file is closed by
1164 the other user, or after 1 second */
1165 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1166 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1167 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1170 if (!NT_STATUS_IS_OK(status)) {
1175 f->handle->have_opendb_entry = True;
1177 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1183 /* do the actual open */
1184 fd = open(f->handle->name->full_name, flags);
1187 return pvfs_map_errno(f->pvfs, errno);
1192 stream_existed = name->stream_exists;
1194 /* if this was a stream create then create the stream as well */
1195 if (!name->stream_exists) {
1196 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1197 if (!NT_STATUS_IS_OK(status)) {
1201 if (stream_truncate) {
1202 status = pvfs_stream_truncate(pvfs, f->handle->name, fd, 0);
1203 if (!NT_STATUS_IS_OK(status)) {
1210 /* re-resolve the open fd */
1211 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1212 if (!NT_STATUS_IS_OK(status)) {
1217 if (f->handle->name->stream_id == 0 &&
1218 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1219 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1220 /* for overwrite we need to replace file permissions */
1221 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1222 mode_t mode = pvfs_fileperms(pvfs, attrib);
1223 if (fchmod(fd, mode) == -1) {
1225 return pvfs_map_errno(pvfs, errno);
1227 name->dos.attrib = attrib;
1228 status = pvfs_dosattrib_save(pvfs, name, fd);
1229 if (!NT_STATUS_IS_OK(status)) {
1237 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1238 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
1240 io->generic.out.oplock_level = OPLOCK_NONE;
1242 io->generic.out.fnum = f->fnum;
1243 io->generic.out.create_action = stream_existed?
1244 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1245 io->generic.out.create_time = name->dos.create_time;
1246 io->generic.out.access_time = name->dos.access_time;
1247 io->generic.out.write_time = name->dos.write_time;
1248 io->generic.out.change_time = name->dos.change_time;
1249 io->generic.out.attrib = name->dos.attrib;
1250 io->generic.out.alloc_size = name->dos.alloc_size;
1251 io->generic.out.size = name->st.st_size;
1252 io->generic.out.file_type = FILE_TYPE_DISK;
1253 io->generic.out.ipc_state = 0;
1254 io->generic.out.is_directory = 0;
1256 /* success - keep the file handle */
1257 talloc_steal(f->pvfs, f);
1259 return NT_STATUS_OK;
1266 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1267 struct ntvfs_request *req, union smb_close *io)
1269 struct pvfs_state *pvfs = ntvfs->private_data;
1270 struct pvfs_file *f;
1271 struct utimbuf unix_times;
1273 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1274 return NT_STATUS_DOS(ERRSRV, ERRerror);
1277 if (io->generic.level != RAW_CLOSE_CLOSE) {
1278 return ntvfs_map_close(ntvfs, req, io);
1281 f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
1283 return NT_STATUS_INVALID_HANDLE;
1286 if (!null_time(io->close.in.write_time)) {
1287 unix_times.actime = 0;
1288 unix_times.modtime = io->close.in.write_time;
1289 utime(f->handle->name->full_name, &unix_times);
1290 } else if (f->handle->sticky_write_time) {
1291 unix_times.actime = 0;
1292 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1293 utime(f->handle->name->full_name, &unix_times);
1298 return NT_STATUS_OK;
1303 logoff - close all file descriptors open by a vuid
1305 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1306 struct ntvfs_request *req)
1308 struct pvfs_state *pvfs = ntvfs->private_data;
1309 struct pvfs_file *f, *next;
1311 for (f=pvfs->open_files;f;f=next) {
1313 if (f->session == req->session) {
1318 return NT_STATUS_OK;
1323 exit - close files for the current pid
1325 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1326 struct ntvfs_request *req)
1328 struct pvfs_state *pvfs = ntvfs->private_data;
1329 struct pvfs_file *f, *next;
1331 for (f=pvfs->open_files;f;f=next) {
1333 if (f->smbpid == req->smbpid) {
1338 return NT_STATUS_OK;
1343 change the delete on close flag on an already open file
1345 NTSTATUS pvfs_set_delete_on_close(struct pvfs_state *pvfs,
1346 struct ntvfs_request *req,
1347 struct pvfs_file *f, BOOL del_on_close)
1349 struct odb_lock *lck;
1352 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) && del_on_close) {
1353 return NT_STATUS_CANNOT_DELETE;
1356 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
1357 !pvfs_directory_empty(pvfs, f->handle->name)) {
1358 return NT_STATUS_DIRECTORY_NOT_EMPTY;
1362 f->handle->create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1364 f->handle->create_options &= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1367 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1369 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1372 status = odb_set_delete_on_close(lck, del_on_close);
1381 determine if a file can be deleted, or if it is prevented by an
1384 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
1385 struct ntvfs_request *req,
1386 struct pvfs_filename *name,
1387 struct odb_lock **lckp)
1391 struct odb_lock *lck;
1393 status = pvfs_locking_key(name, name, &key);
1394 if (!NT_STATUS_IS_OK(status)) {
1395 return NT_STATUS_NO_MEMORY;
1398 lck = odb_lock(req, pvfs->odb_context, &key);
1400 DEBUG(0,("Unable to lock opendb for can_delete\n"));
1401 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1404 status = odb_can_open(lck,
1405 NTCREATEX_SHARE_ACCESS_READ |
1406 NTCREATEX_SHARE_ACCESS_WRITE |
1407 NTCREATEX_SHARE_ACCESS_DELETE,
1408 NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
1411 if (NT_STATUS_IS_OK(status)) {
1412 status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
1415 if (!NT_STATUS_IS_OK(status)) {
1418 } else if (lckp != NULL) {
1426 determine if a file can be renamed, or if it is prevented by an
1429 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
1430 struct ntvfs_request *req,
1431 struct pvfs_filename *name,
1432 struct odb_lock **lckp)
1436 struct odb_lock *lck;
1438 status = pvfs_locking_key(name, name, &key);
1439 if (!NT_STATUS_IS_OK(status)) {
1440 return NT_STATUS_NO_MEMORY;
1443 lck = odb_lock(req, pvfs->odb_context, &key);
1445 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1446 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1449 status = odb_can_open(lck,
1450 NTCREATEX_SHARE_ACCESS_READ |
1451 NTCREATEX_SHARE_ACCESS_WRITE,
1455 if (!NT_STATUS_IS_OK(status)) {
1458 } else if (lckp != NULL) {
1466 determine if file meta data can be accessed, or if it is prevented by an
1469 NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
1470 struct ntvfs_request *req,
1471 struct pvfs_filename *name)
1475 struct odb_lock *lck;
1477 status = pvfs_locking_key(name, name, &key);
1478 if (!NT_STATUS_IS_OK(status)) {
1479 return NT_STATUS_NO_MEMORY;
1482 lck = odb_lock(req, pvfs->odb_context, &key);
1484 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1485 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1488 status = odb_can_open(lck,
1489 NTCREATEX_SHARE_ACCESS_READ |
1490 NTCREATEX_SHARE_ACCESS_WRITE,
1498 determine if delete on close is set on
1500 BOOL pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h,
1501 int *open_count, char **path)
1506 status = odb_get_delete_on_close(pvfs->odb_context, &h->odb_locking_key,
1507 &del_on_close, open_count, path);
1508 if (!NT_STATUS_IS_OK(status)) {
1509 DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
1513 return del_on_close;