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/time.h"
26 #include "dlinklist.h"
28 #include "librpc/gen_ndr/ndr_xattr.h"
31 create file handles with convenient numbers for sniffers
33 #define PVFS_MIN_FILE_FNUM 0x100
34 #define PVFS_MIN_NEW_FNUM 0x200
35 #define PVFS_MIN_DIR_FNUM 0x300
38 find open file handle given fnum
40 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
41 struct smbsrv_request *req, uint16_t fnum)
45 f = idr_find(pvfs->idtree_fnum, fnum);
50 if (f->fnum != fnum) {
51 smb_panic("pvfs_find_fd: idtree_fnum corruption\n");
54 if (req->session != f->session) {
55 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n",
65 cleanup a open directory handle
67 static int pvfs_dir_handle_destructor(void *p)
69 struct pvfs_file_handle *h = p;
71 if (h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
72 NTSTATUS status = pvfs_xattr_unlink_hook(h->pvfs, h->name->full_name);
73 if (!NT_STATUS_IS_OK(status)) {
74 DEBUG(0,("Warning: xattr rmdir hook failed for '%s' - %s\n",
75 h->name->full_name, nt_errstr(status)));
77 if (rmdir(h->name->full_name) != 0) {
78 DEBUG(0,("pvfs_close: failed to rmdir '%s' - %s\n",
79 h->name->full_name, strerror(errno)));
87 cleanup a open directory fnum
89 static int pvfs_dir_fnum_destructor(void *p)
91 struct pvfs_file *f = p;
92 DLIST_REMOVE(f->pvfs->open_files, f);
93 idr_remove(f->pvfs->idtree_fnum, f->fnum);
98 setup any EAs and the ACL on newly created files/directories
100 static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
101 struct smbsrv_request *req,
102 struct pvfs_filename *name,
108 /* setup any EAs that were asked for */
109 if (io->ntcreatex.in.ea_list) {
110 status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
111 io->ntcreatex.in.ea_list->num_eas,
112 io->ntcreatex.in.ea_list->eas);
113 if (!NT_STATUS_IS_OK(status)) {
118 /* setup an initial sec_desc if requested */
119 if (io->ntcreatex.in.sec_desc) {
120 union smb_setfileinfo set;
122 set.set_secdesc.file.fnum = fnum;
123 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
124 set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
126 status = pvfs_acl_set(pvfs, req, name, fd, SEC_STD_WRITE_DAC, &set);
128 /* otherwise setup an inherited acl from the parent */
129 status = pvfs_acl_inherit(pvfs, req, name, fd);
138 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
139 struct smbsrv_request *req,
140 struct pvfs_filename *name,
146 uint32_t create_action;
147 uint32_t access_mask = io->generic.in.access_mask;
149 if (name->stream_name) {
150 return NT_STATUS_NOT_A_DIRECTORY;
153 /* if the client says it must be a directory, and it isn't,
155 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
156 return NT_STATUS_NOT_A_DIRECTORY;
159 switch (io->generic.in.open_disposition) {
160 case NTCREATEX_DISP_OPEN_IF:
163 case NTCREATEX_DISP_OPEN:
165 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
169 case NTCREATEX_DISP_CREATE:
171 return NT_STATUS_OBJECT_NAME_COLLISION;
175 case NTCREATEX_DISP_OVERWRITE_IF:
176 case NTCREATEX_DISP_OVERWRITE:
177 case NTCREATEX_DISP_SUPERSEDE:
179 return NT_STATUS_INVALID_PARAMETER;
182 f = talloc(req, struct pvfs_file);
184 return NT_STATUS_NO_MEMORY;
187 f->handle = talloc(f, struct pvfs_file_handle);
188 if (f->handle == NULL) {
189 return NT_STATUS_NO_MEMORY;
192 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
194 return NT_STATUS_TOO_MANY_OPENED_FILES;
198 /* check the security descriptor */
199 status = pvfs_access_check(pvfs, req, name, &access_mask);
201 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
203 if (!NT_STATUS_IS_OK(status)) {
204 idr_remove(pvfs->idtree_fnum, fnum);
209 f->session = req->session;
210 f->smbpid = req->smbpid;
212 f->pending_list = NULL;
214 f->share_access = io->generic.in.share_access;
215 f->impersonation = io->generic.in.impersonation;
216 f->access_mask = access_mask;
218 f->handle->pvfs = pvfs;
219 f->handle->name = talloc_steal(f->handle, name);
221 f->handle->odb_locking_key = data_blob(NULL, 0);
222 f->handle->brl_locking_key = data_blob(NULL, 0);
223 f->handle->create_options = io->generic.in.create_options;
224 f->handle->seek_offset = 0;
225 f->handle->position = 0;
227 f->handle->sticky_write_time = False;
229 DLIST_ADD(pvfs->open_files, f);
231 /* TODO: should we check in the opendb? Do directory opens
232 follow the share_access rules? */
234 /* setup destructors to avoid leaks on abnormal termination */
235 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
236 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
239 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
240 mode_t mode = pvfs_fileperms(pvfs, attrib);
241 if (mkdir(name->full_name, mode) == -1) {
242 idr_remove(pvfs->idtree_fnum, fnum);
243 return pvfs_map_errno(pvfs,errno);
246 pvfs_xattr_unlink_hook(pvfs, name->full_name);
248 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
249 if (!NT_STATUS_IS_OK(status)) {
253 status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, fnum, io);
254 if (!NT_STATUS_IS_OK(status)) {
258 create_action = NTCREATEX_ACTION_CREATED;
260 create_action = NTCREATEX_ACTION_EXISTED;
264 idr_remove(pvfs->idtree_fnum, fnum);
265 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
268 /* the open succeeded, keep this handle permanently */
269 talloc_steal(pvfs, f);
271 io->generic.out.oplock_level = OPLOCK_NONE;
272 io->generic.out.fnum = f->fnum;
273 io->generic.out.create_action = create_action;
274 io->generic.out.create_time = name->dos.create_time;
275 io->generic.out.access_time = name->dos.access_time;
276 io->generic.out.write_time = name->dos.write_time;
277 io->generic.out.change_time = name->dos.change_time;
278 io->generic.out.attrib = name->dos.attrib;
279 io->generic.out.alloc_size = name->dos.alloc_size;
280 io->generic.out.size = name->st.st_size;
281 io->generic.out.file_type = FILE_TYPE_DISK;
282 io->generic.out.ipc_state = 0;
283 io->generic.out.is_directory = 1;
288 idr_remove(pvfs->idtree_fnum, fnum);
289 rmdir(name->full_name);
294 destroy a struct pvfs_file_handle
296 static int pvfs_handle_destructor(void *p)
298 struct pvfs_file_handle *h = p;
300 /* the write time is no longer sticky */
301 if (h->sticky_write_time) {
303 status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
304 if (NT_STATUS_IS_OK(status)) {
305 h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
306 pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
310 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
311 h->name->stream_name) {
313 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
314 if (!NT_STATUS_IS_OK(status)) {
315 DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
316 h->name->stream_name, h->name->full_name));
321 if (close(h->fd) != 0) {
322 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
323 h->fd, h->name->full_name, strerror(errno)));
328 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
329 h->name->stream_name == NULL) {
331 status = pvfs_xattr_unlink_hook(h->pvfs, h->name->full_name);
332 if (!NT_STATUS_IS_OK(status)) {
333 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
334 h->name->full_name, nt_errstr(status)));
336 if (unlink(h->name->full_name) != 0) {
337 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
338 h->name->full_name, strerror(errno)));
342 if (h->have_opendb_entry) {
343 struct odb_lock *lck;
346 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
348 DEBUG(0,("Unable to lock opendb for close\n"));
352 status = odb_close_file(lck, h);
353 if (!NT_STATUS_IS_OK(status)) {
354 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
355 h->name->full_name, nt_errstr(status)));
366 destroy a struct pvfs_file
368 static int pvfs_fnum_destructor(void *p)
370 struct pvfs_file *f = p;
372 DLIST_REMOVE(f->pvfs->open_files, f);
373 pvfs_lock_close(f->pvfs, f);
374 idr_remove(f->pvfs->idtree_fnum, f->fnum);
381 form the lock context used for opendb locking. Note that we must
382 zero here to take account of possible padding on some architectures
384 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
385 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
391 ZERO_STRUCT(lock_context);
393 lock_context.device = name->st.st_dev;
394 lock_context.inode = name->st.st_ino;
396 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
397 if (key->data == NULL) {
398 return NT_STATUS_NO_MEMORY;
405 form the lock context used for byte range locking. This is separate
406 from the locking key used for opendb locking as it needs to take
407 account of file streams (each stream is a separate byte range
410 static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name,
411 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
415 status = pvfs_locking_key(name, mem_ctx, &odb_key);
416 if (!NT_STATUS_IS_OK(status)) {
419 if (name->stream_name == NULL) {
423 *key = data_blob_talloc(mem_ctx, NULL,
424 odb_key.length + strlen(name->stream_name) + 1);
425 if (key->data == NULL) {
426 return NT_STATUS_NO_MEMORY;
428 memcpy(key->data, odb_key.data, odb_key.length);
429 memcpy(key->data + odb_key.length,
430 name->stream_name, strlen(name->stream_name)+1);
431 data_blob_free(&odb_key);
439 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
440 struct smbsrv_request *req,
441 struct pvfs_filename *name,
447 struct odb_lock *lck;
448 uint32_t create_options = io->generic.in.create_options;
449 uint32_t share_access = io->generic.in.share_access;
450 uint32_t access_mask = io->generic.in.access_mask;
454 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
455 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
456 return NT_STATUS_CANNOT_DELETE;
459 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
460 if (!NT_STATUS_IS_OK(status)) {
464 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
470 f = talloc(req, struct pvfs_file);
472 return NT_STATUS_NO_MEMORY;
475 f->handle = talloc(f, struct pvfs_file_handle);
476 if (f->handle == NULL) {
477 return NT_STATUS_NO_MEMORY;
480 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
482 return NT_STATUS_TOO_MANY_OPENED_FILES;
485 attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
486 mode = pvfs_fileperms(pvfs, attrib);
488 /* create the file */
489 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
491 idr_remove(pvfs->idtree_fnum, fnum);
492 return pvfs_map_errno(pvfs, errno);
495 pvfs_xattr_unlink_hook(pvfs, name->full_name);
497 /* if this was a stream create then create the stream as well */
498 if (name->stream_name) {
499 status = pvfs_stream_create(pvfs, name, fd);
500 if (!NT_STATUS_IS_OK(status)) {
501 idr_remove(pvfs->idtree_fnum, fnum);
507 /* re-resolve the open fd */
508 status = pvfs_resolve_name_fd(pvfs, fd, name);
509 if (!NT_STATUS_IS_OK(status)) {
510 idr_remove(pvfs->idtree_fnum, fnum);
515 name->dos.attrib = attrib;
516 status = pvfs_dosattrib_save(pvfs, name, fd);
517 if (!NT_STATUS_IS_OK(status)) {
522 status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, fnum, io);
523 if (!NT_STATUS_IS_OK(status)) {
527 /* form the lock context used for byte range locking and
529 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
530 if (!NT_STATUS_IS_OK(status)) {
534 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
535 if (!NT_STATUS_IS_OK(status)) {
539 /* grab a lock on the open file record */
540 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
542 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
544 /* we were supposed to do a blocking lock, so something
546 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
550 status = odb_open_file(lck, f->handle, name->stream_id,
551 share_access, create_options, access_mask);
553 if (!NT_STATUS_IS_OK(status)) {
554 /* bad news, we must have hit a race - we don't delete the file
555 here as the most likely scenario is that someone else created
556 the file at the same time */
557 idr_remove(pvfs->idtree_fnum, fnum);
563 f->session = req->session;
564 f->smbpid = req->smbpid;
566 f->pending_list = NULL;
568 f->share_access = io->generic.in.share_access;
569 f->access_mask = access_mask;
570 f->impersonation = io->generic.in.impersonation;
572 f->handle->pvfs = pvfs;
573 f->handle->name = talloc_steal(f->handle, name);
575 f->handle->create_options = io->generic.in.create_options;
576 f->handle->seek_offset = 0;
577 f->handle->position = 0;
579 f->handle->have_opendb_entry = True;
580 f->handle->sticky_write_time = False;
582 DLIST_ADD(pvfs->open_files, f);
584 /* setup a destructor to avoid file descriptor leaks on
585 abnormal termination */
586 talloc_set_destructor(f, pvfs_fnum_destructor);
587 talloc_set_destructor(f->handle, pvfs_handle_destructor);
590 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
591 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
593 io->generic.out.oplock_level = OPLOCK_NONE;
595 io->generic.out.fnum = f->fnum;
596 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
597 io->generic.out.create_time = name->dos.create_time;
598 io->generic.out.access_time = name->dos.access_time;
599 io->generic.out.write_time = name->dos.write_time;
600 io->generic.out.change_time = name->dos.change_time;
601 io->generic.out.attrib = name->dos.attrib;
602 io->generic.out.alloc_size = name->dos.alloc_size;
603 io->generic.out.size = name->st.st_size;
604 io->generic.out.file_type = FILE_TYPE_DISK;
605 io->generic.out.ipc_state = 0;
606 io->generic.out.is_directory = 0;
608 /* success - keep the file handle */
609 talloc_steal(pvfs, f);
614 idr_remove(pvfs->idtree_fnum, fnum);
616 unlink(name->full_name);
622 state of a pending open retry
624 struct pvfs_open_retry {
625 struct ntvfs_module_context *ntvfs;
626 struct smbsrv_request *req;
629 DATA_BLOB odb_locking_key;
632 /* destroy a pending open request */
633 static int pvfs_retry_destructor(void *ptr)
635 struct pvfs_open_retry *r = ptr;
636 struct pvfs_state *pvfs = r->ntvfs->private_data;
637 if (r->odb_locking_key.data) {
638 struct odb_lock *lck;
639 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
641 odb_remove_pending(lck, r);
651 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
653 struct pvfs_open_retry *r = private;
654 struct ntvfs_module_context *ntvfs = r->ntvfs;
655 struct smbsrv_request *req = r->req;
656 union smb_open *io = r->io;
659 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
660 just a bug in their server, but we better do the same */
661 if (reason == PVFS_WAIT_CANCEL) {
665 talloc_free(r->wait_handle);
667 if (reason == PVFS_WAIT_TIMEOUT) {
668 /* if it timed out, then give the failure
671 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
672 req->async_states->send_fn(req);
676 /* the pending odb entry is already removed. We use a null locking
677 key to indicate this */
678 data_blob_free(&r->odb_locking_key);
681 /* try the open again, which could trigger another retry setup
682 if it wants to, so we have to unmark the async flag so we
683 will know if it does a second async reply */
684 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
686 status = pvfs_open(ntvfs, req, io);
687 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
688 /* the 2nd try also replied async, so we don't send
693 /* re-mark it async, just in case someone up the chain does
695 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
697 /* send the reply up the chain */
698 req->async_states->status = status;
699 req->async_states->send_fn(req);
704 special handling for openx DENY_DOS semantics
706 This function attempts a reference open using an existing handle. If its allowed,
707 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
708 open processing continues.
710 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
711 struct smbsrv_request *req, union smb_open *io,
712 struct pvfs_file *f, struct odb_lock *lck)
714 struct pvfs_state *pvfs = ntvfs->private_data;
715 struct pvfs_file *f2;
716 struct pvfs_filename *name;
718 /* search for an existing open with the right parameters. Note
719 the magic ntcreatex options flag, which is set in the
720 generic mapping code. This might look ugly, but its
721 actually pretty much now w2k does it internally as well.
723 If you look at the BASE-DENYDOS test you will see that a
724 DENY_DOS is a very special case, and in the right
725 circumstances you actually get the _same_ handle back
726 twice, rather than a new handle.
728 for (f2=pvfs->open_files;f2;f2=f2->next) {
730 f2->session == req->session &&
731 f2->smbpid == req->smbpid &&
732 (f2->handle->create_options &
733 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
734 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
735 (f2->access_mask & SEC_FILE_WRITE_DATA) &&
736 StrCaseCmp(f2->handle->name->original_name,
737 io->generic.in.fname)==0) {
743 return NT_STATUS_SHARING_VIOLATION;
746 /* quite an insane set of semantics ... */
747 if (is_exe_filename(io->generic.in.fname) &&
748 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
749 return NT_STATUS_SHARING_VIOLATION;
753 setup a reference to the existing handle
755 talloc_free(f->handle);
756 f->handle = talloc_reference(f, f2->handle);
760 name = f->handle->name;
762 io->generic.out.oplock_level = OPLOCK_NONE;
763 io->generic.out.fnum = f->fnum;
764 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
765 io->generic.out.create_time = name->dos.create_time;
766 io->generic.out.access_time = name->dos.access_time;
767 io->generic.out.write_time = name->dos.write_time;
768 io->generic.out.change_time = name->dos.change_time;
769 io->generic.out.attrib = name->dos.attrib;
770 io->generic.out.alloc_size = name->dos.alloc_size;
771 io->generic.out.size = name->st.st_size;
772 io->generic.out.file_type = FILE_TYPE_DISK;
773 io->generic.out.ipc_state = 0;
774 io->generic.out.is_directory = 0;
776 talloc_steal(f->pvfs, f);
784 setup for a open retry after a sharing violation
786 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
787 struct smbsrv_request *req,
790 struct odb_lock *lck)
792 struct pvfs_state *pvfs = ntvfs->private_data;
793 struct pvfs_open_retry *r;
795 struct timeval end_time;
797 if (io->generic.in.create_options &
798 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
799 /* see if we can satisfy the request using the special DENY_DOS
801 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
802 if (NT_STATUS_IS_OK(status)) {
807 r = talloc(req, struct pvfs_open_retry);
809 return NT_STATUS_NO_MEMORY;
815 r->odb_locking_key = data_blob_talloc(r,
816 f->handle->odb_locking_key.data,
817 f->handle->odb_locking_key.length);
819 end_time = timeval_add(&req->request_time, 0, pvfs->sharing_violation_delay);
821 /* setup a pending lock */
822 status = odb_open_file_pending(lck, r);
823 if (!NT_STATUS_IS_OK(status)) {
830 talloc_set_destructor(r, pvfs_retry_destructor);
832 r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
834 if (r->wait_handle == NULL) {
835 return NT_STATUS_NO_MEMORY;
838 talloc_steal(pvfs, r);
846 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
847 struct smbsrv_request *req, union smb_open *io)
849 struct pvfs_state *pvfs = ntvfs->private_data;
851 struct pvfs_filename *name;
855 struct odb_lock *lck;
856 uint32_t create_options;
857 uint32_t share_access;
858 uint32_t access_mask;
859 BOOL stream_existed, stream_truncate=False;
861 /* use the generic mapping code to avoid implementing all the
862 different open calls. */
863 if (io->generic.level != RAW_OPEN_GENERIC &&
864 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
865 return ntvfs_map_open(req, io, ntvfs);
868 /* resolve the cifs name to a posix name */
869 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
870 PVFS_RESOLVE_STREAMS, &name);
871 if (!NT_STATUS_IS_OK(status)) {
875 /* directory opens are handled separately */
876 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
877 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
878 return pvfs_open_directory(pvfs, req, name, io);
881 /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
882 open doesn't match */
883 io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
885 create_options = io->generic.in.create_options;
886 share_access = io->generic.in.share_access;
887 access_mask = io->generic.in.access_mask;
889 /* certain create options are not allowed */
890 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
891 !(access_mask & SEC_STD_DELETE)) {
892 return NT_STATUS_INVALID_PARAMETER;
897 switch (io->generic.in.open_disposition) {
898 case NTCREATEX_DISP_SUPERSEDE:
899 case NTCREATEX_DISP_OVERWRITE_IF:
900 if (name->stream_name == NULL) {
903 stream_truncate = True;
907 case NTCREATEX_DISP_OPEN:
908 if (!name->stream_exists) {
909 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
914 case NTCREATEX_DISP_OVERWRITE:
915 if (!name->stream_exists) {
916 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
918 if (name->stream_name == NULL) {
921 stream_truncate = True;
925 case NTCREATEX_DISP_CREATE:
926 if (name->stream_exists) {
927 return NT_STATUS_OBJECT_NAME_COLLISION;
932 case NTCREATEX_DISP_OPEN_IF:
937 return NT_STATUS_INVALID_PARAMETER;
940 /* handle creating a new file separately */
942 status = pvfs_create_file(pvfs, req, name, io);
943 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
947 /* we've hit a race - the file was created during this call */
948 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
952 /* try re-resolving the name */
953 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
954 if (!NT_STATUS_IS_OK(status)) {
957 /* fall through to a normal open */
960 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
961 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
962 return NT_STATUS_CANNOT_DELETE;
965 /* check the security descriptor */
966 status = pvfs_access_check(pvfs, req, name, &access_mask);
967 if (!NT_STATUS_IS_OK(status)) {
971 f = talloc(req, struct pvfs_file);
973 return NT_STATUS_NO_MEMORY;
976 f->handle = talloc(f, struct pvfs_file_handle);
977 if (f->handle == NULL) {
978 return NT_STATUS_NO_MEMORY;
981 /* allocate a fnum */
982 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
984 return NT_STATUS_TOO_MANY_OPENED_FILES;
988 f->session = req->session;
989 f->smbpid = req->smbpid;
991 f->pending_list = NULL;
993 f->share_access = io->generic.in.share_access;
994 f->access_mask = access_mask;
995 f->impersonation = io->generic.in.impersonation;
997 f->handle->pvfs = pvfs;
999 f->handle->name = talloc_steal(f->handle, name);
1000 f->handle->create_options = io->generic.in.create_options;
1001 f->handle->seek_offset = 0;
1002 f->handle->position = 0;
1003 f->handle->mode = 0;
1004 f->handle->have_opendb_entry = False;
1005 f->handle->sticky_write_time = False;
1007 /* form the lock context used for byte range locking and
1009 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
1010 if (!NT_STATUS_IS_OK(status)) {
1011 idr_remove(pvfs->idtree_fnum, f->fnum);
1015 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
1016 if (!NT_STATUS_IS_OK(status)) {
1017 idr_remove(pvfs->idtree_fnum, f->fnum);
1021 /* get a lock on this file before the actual open */
1022 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1024 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1026 /* we were supposed to do a blocking lock, so something
1028 idr_remove(pvfs->idtree_fnum, fnum);
1029 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1032 DLIST_ADD(pvfs->open_files, f);
1034 /* setup a destructor to avoid file descriptor leaks on
1035 abnormal termination */
1036 talloc_set_destructor(f, pvfs_fnum_destructor);
1037 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1040 /* see if we are allowed to open at the same time as existing opens */
1041 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
1042 share_access, create_options, access_mask);
1044 /* on a sharing violation we need to retry when the file is closed by
1045 the other user, or after 1 second */
1046 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1047 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1048 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1051 if (!NT_STATUS_IS_OK(status)) {
1056 f->handle->have_opendb_entry = True;
1058 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1064 /* do the actual open */
1065 fd = open(f->handle->name->full_name, flags);
1068 return pvfs_map_errno(f->pvfs, errno);
1073 stream_existed = name->stream_exists;
1075 /* if this was a stream create then create the stream as well */
1076 if (!name->stream_exists) {
1077 if (!(access_mask & SEC_FILE_WRITE_ATTRIBUTE)) {
1078 return NT_STATUS_ACCESS_DENIED;
1080 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1081 if (!NT_STATUS_IS_OK(status)) {
1085 if (stream_truncate) {
1086 status = pvfs_stream_truncate(pvfs, f->handle->name, fd, 0);
1087 if (!NT_STATUS_IS_OK(status)) {
1094 /* re-resolve the open fd */
1095 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1096 if (!NT_STATUS_IS_OK(status)) {
1101 if (f->handle->name->stream_id == 0 &&
1102 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1103 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1104 /* for overwrite we need to replace file permissions */
1105 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1106 mode_t mode = pvfs_fileperms(pvfs, attrib);
1107 if (fchmod(fd, mode) == -1) {
1109 return pvfs_map_errno(pvfs, errno);
1111 name->dos.attrib = attrib;
1112 status = pvfs_dosattrib_save(pvfs, name, fd);
1113 if (!NT_STATUS_IS_OK(status)) {
1121 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1122 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
1124 io->generic.out.oplock_level = OPLOCK_NONE;
1126 io->generic.out.fnum = f->fnum;
1127 io->generic.out.create_action = stream_existed?
1128 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1129 io->generic.out.create_time = name->dos.create_time;
1130 io->generic.out.access_time = name->dos.access_time;
1131 io->generic.out.write_time = name->dos.write_time;
1132 io->generic.out.change_time = name->dos.change_time;
1133 io->generic.out.attrib = name->dos.attrib;
1134 io->generic.out.alloc_size = name->dos.alloc_size;
1135 io->generic.out.size = name->st.st_size;
1136 io->generic.out.file_type = FILE_TYPE_DISK;
1137 io->generic.out.ipc_state = 0;
1138 io->generic.out.is_directory = 0;
1140 /* success - keep the file handle */
1141 talloc_steal(f->pvfs, f);
1143 return NT_STATUS_OK;
1150 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1151 struct smbsrv_request *req, union smb_close *io)
1153 struct pvfs_state *pvfs = ntvfs->private_data;
1154 struct pvfs_file *f;
1155 struct utimbuf unix_times;
1157 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1158 return NT_STATUS_UNSUCCESSFUL;
1161 if (io->generic.level != RAW_CLOSE_CLOSE) {
1162 return ntvfs_map_close(req, io, ntvfs);
1165 f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
1167 return NT_STATUS_INVALID_HANDLE;
1170 if (!null_time(io->close.in.write_time)) {
1171 unix_times.actime = 0;
1172 unix_times.modtime = io->close.in.write_time;
1173 utime(f->handle->name->full_name, &unix_times);
1174 } else if (f->handle->sticky_write_time) {
1175 unix_times.actime = 0;
1176 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1177 utime(f->handle->name->full_name, &unix_times);
1182 return NT_STATUS_OK;
1187 logoff - close all file descriptors open by a vuid
1189 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1190 struct smbsrv_request *req)
1192 struct pvfs_state *pvfs = ntvfs->private_data;
1193 struct pvfs_file *f, *next;
1195 for (f=pvfs->open_files;f;f=next) {
1197 if (f->session == req->session) {
1202 return NT_STATUS_OK;
1207 exit - close files for the current pid
1209 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1210 struct smbsrv_request *req)
1212 struct pvfs_state *pvfs = ntvfs->private_data;
1213 struct pvfs_file *f, *next;
1215 for (f=pvfs->open_files;f;f=next) {
1217 if (f->smbpid == req->smbpid) {
1222 return NT_STATUS_OK;
1227 change the create options on an already open file
1229 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
1230 struct smbsrv_request *req,
1231 struct pvfs_file *f, uint32_t create_options)
1233 struct odb_lock *lck;
1236 if (f->handle->create_options == create_options) {
1237 return NT_STATUS_OK;
1240 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1241 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1242 return NT_STATUS_CANNOT_DELETE;
1245 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1247 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1250 status = odb_set_create_options(lck, f->handle, create_options);
1251 if (NT_STATUS_IS_OK(status)) {
1252 f->handle->create_options = create_options;
1260 determine if a file can be deleted, or if it is prevented by an
1263 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
1264 struct smbsrv_request *req,
1265 struct pvfs_filename *name)
1270 status = pvfs_locking_key(name, name, &key);
1271 if (!NT_STATUS_IS_OK(status)) {
1272 return NT_STATUS_NO_MEMORY;
1275 status = odb_can_open(pvfs->odb_context, &key,
1276 NTCREATEX_SHARE_ACCESS_READ |
1277 NTCREATEX_SHARE_ACCESS_WRITE |
1278 NTCREATEX_SHARE_ACCESS_DELETE,
1279 NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
1282 if (NT_STATUS_IS_OK(status)) {
1283 status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
1290 determine if a file can be renamed, or if it is prevented by an
1293 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs, struct pvfs_filename *name)
1298 status = pvfs_locking_key(name, name, &key);
1299 if (!NT_STATUS_IS_OK(status)) {
1300 return NT_STATUS_NO_MEMORY;
1303 status = odb_can_open(pvfs->odb_context, &key,
1304 NTCREATEX_SHARE_ACCESS_READ |
1305 NTCREATEX_SHARE_ACCESS_WRITE,