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;
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;
895 switch (io->generic.in.open_disposition) {
896 case NTCREATEX_DISP_SUPERSEDE:
900 case NTCREATEX_DISP_OVERWRITE_IF:
904 case NTCREATEX_DISP_OPEN:
905 if (!name->stream_exists) {
906 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
911 case NTCREATEX_DISP_OVERWRITE:
912 if (!name->stream_exists) {
913 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
918 case NTCREATEX_DISP_CREATE:
919 if (name->stream_exists) {
920 return NT_STATUS_OBJECT_NAME_COLLISION;
925 case NTCREATEX_DISP_OPEN_IF:
930 return NT_STATUS_INVALID_PARAMETER;
933 /* handle creating a new file separately */
935 status = pvfs_create_file(pvfs, req, name, io);
936 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
940 /* we've hit a race - the file was created during this call */
941 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
945 /* try re-resolving the name */
946 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
947 if (!NT_STATUS_IS_OK(status)) {
950 /* fall through to a normal open */
953 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
954 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
955 return NT_STATUS_CANNOT_DELETE;
958 /* check the security descriptor */
959 status = pvfs_access_check(pvfs, req, name, &access_mask);
960 if (!NT_STATUS_IS_OK(status)) {
964 f = talloc(req, struct pvfs_file);
966 return NT_STATUS_NO_MEMORY;
969 f->handle = talloc(f, struct pvfs_file_handle);
970 if (f->handle == NULL) {
971 return NT_STATUS_NO_MEMORY;
974 /* allocate a fnum */
975 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
977 return NT_STATUS_TOO_MANY_OPENED_FILES;
981 f->session = req->session;
982 f->smbpid = req->smbpid;
984 f->pending_list = NULL;
986 f->share_access = io->generic.in.share_access;
987 f->access_mask = access_mask;
988 f->impersonation = io->generic.in.impersonation;
990 f->handle->pvfs = pvfs;
992 f->handle->name = talloc_steal(f->handle, name);
993 f->handle->create_options = io->generic.in.create_options;
994 f->handle->seek_offset = 0;
995 f->handle->position = 0;
997 f->handle->have_opendb_entry = False;
998 f->handle->sticky_write_time = False;
1000 /* form the lock context used for byte range locking and
1002 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
1003 if (!NT_STATUS_IS_OK(status)) {
1004 idr_remove(pvfs->idtree_fnum, f->fnum);
1008 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
1009 if (!NT_STATUS_IS_OK(status)) {
1010 idr_remove(pvfs->idtree_fnum, f->fnum);
1014 /* get a lock on this file before the actual open */
1015 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1017 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1019 /* we were supposed to do a blocking lock, so something
1021 idr_remove(pvfs->idtree_fnum, fnum);
1022 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1025 DLIST_ADD(pvfs->open_files, f);
1027 /* setup a destructor to avoid file descriptor leaks on
1028 abnormal termination */
1029 talloc_set_destructor(f, pvfs_fnum_destructor);
1030 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1033 /* see if we are allowed to open at the same time as existing opens */
1034 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
1035 share_access, create_options, access_mask);
1037 /* on a sharing violation we need to retry when the file is closed by
1038 the other user, or after 1 second */
1039 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1040 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1041 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1044 if (!NT_STATUS_IS_OK(status)) {
1049 f->handle->have_opendb_entry = True;
1051 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1057 /* do the actual open */
1058 fd = open(f->handle->name->full_name, flags);
1061 return pvfs_map_errno(f->pvfs, errno);
1066 stream_existed = name->stream_exists;
1068 /* if this was a stream create then create the stream as well */
1069 if (!name->stream_exists) {
1070 if (!(access_mask & SEC_FILE_WRITE_ATTRIBUTE)) {
1071 return NT_STATUS_ACCESS_DENIED;
1073 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1074 if (!NT_STATUS_IS_OK(status)) {
1080 /* re-resolve the open fd */
1081 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1082 if (!NT_STATUS_IS_OK(status)) {
1087 if (f->handle->name->stream_id == 0 &&
1088 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1089 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1090 /* for overwrite we need to replace file permissions */
1091 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1092 mode_t mode = pvfs_fileperms(pvfs, attrib);
1093 if (fchmod(fd, mode) == -1) {
1095 return pvfs_map_errno(pvfs, errno);
1097 name->dos.attrib = attrib;
1098 status = pvfs_dosattrib_save(pvfs, name, fd);
1099 if (!NT_STATUS_IS_OK(status)) {
1107 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1108 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
1110 io->generic.out.oplock_level = OPLOCK_NONE;
1112 io->generic.out.fnum = f->fnum;
1113 io->generic.out.create_action = stream_existed?
1114 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1115 io->generic.out.create_time = name->dos.create_time;
1116 io->generic.out.access_time = name->dos.access_time;
1117 io->generic.out.write_time = name->dos.write_time;
1118 io->generic.out.change_time = name->dos.change_time;
1119 io->generic.out.attrib = name->dos.attrib;
1120 io->generic.out.alloc_size = name->dos.alloc_size;
1121 io->generic.out.size = name->st.st_size;
1122 io->generic.out.file_type = FILE_TYPE_DISK;
1123 io->generic.out.ipc_state = 0;
1124 io->generic.out.is_directory = 0;
1126 /* success - keep the file handle */
1127 talloc_steal(f->pvfs, f);
1129 return NT_STATUS_OK;
1136 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1137 struct smbsrv_request *req, union smb_close *io)
1139 struct pvfs_state *pvfs = ntvfs->private_data;
1140 struct pvfs_file *f;
1141 struct utimbuf unix_times;
1143 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1144 return NT_STATUS_UNSUCCESSFUL;
1147 if (io->generic.level != RAW_CLOSE_CLOSE) {
1148 return ntvfs_map_close(req, io, ntvfs);
1151 f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
1153 return NT_STATUS_INVALID_HANDLE;
1156 if (!null_time(io->close.in.write_time)) {
1157 unix_times.actime = 0;
1158 unix_times.modtime = io->close.in.write_time;
1159 utime(f->handle->name->full_name, &unix_times);
1160 } else if (f->handle->sticky_write_time) {
1161 unix_times.actime = 0;
1162 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1163 utime(f->handle->name->full_name, &unix_times);
1168 return NT_STATUS_OK;
1173 logoff - close all file descriptors open by a vuid
1175 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1176 struct smbsrv_request *req)
1178 struct pvfs_state *pvfs = ntvfs->private_data;
1179 struct pvfs_file *f, *next;
1181 for (f=pvfs->open_files;f;f=next) {
1183 if (f->session == req->session) {
1188 return NT_STATUS_OK;
1193 exit - close files for the current pid
1195 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1196 struct smbsrv_request *req)
1198 struct pvfs_state *pvfs = ntvfs->private_data;
1199 struct pvfs_file *f, *next;
1201 for (f=pvfs->open_files;f;f=next) {
1203 if (f->smbpid == req->smbpid) {
1208 return NT_STATUS_OK;
1213 change the create options on an already open file
1215 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
1216 struct smbsrv_request *req,
1217 struct pvfs_file *f, uint32_t create_options)
1219 struct odb_lock *lck;
1222 if (f->handle->create_options == create_options) {
1223 return NT_STATUS_OK;
1226 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1227 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1228 return NT_STATUS_CANNOT_DELETE;
1231 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1233 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1236 status = odb_set_create_options(lck, f->handle, create_options);
1237 if (NT_STATUS_IS_OK(status)) {
1238 f->handle->create_options = create_options;
1246 determine if a file can be deleted, or if it is prevented by an
1249 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
1250 struct smbsrv_request *req,
1251 struct pvfs_filename *name)
1256 status = pvfs_locking_key(name, name, &key);
1257 if (!NT_STATUS_IS_OK(status)) {
1258 return NT_STATUS_NO_MEMORY;
1261 status = odb_can_open(pvfs->odb_context, &key,
1262 NTCREATEX_SHARE_ACCESS_READ |
1263 NTCREATEX_SHARE_ACCESS_WRITE |
1264 NTCREATEX_SHARE_ACCESS_DELETE,
1265 NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
1268 if (NT_STATUS_IS_OK(status)) {
1269 status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
1276 determine if a file can be renamed, or if it is prevented by an
1279 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs, struct pvfs_filename *name)
1284 status = pvfs_locking_key(name, name, &key);
1285 if (!NT_STATUS_IS_OK(status)) {
1286 return NT_STATUS_NO_MEMORY;
1289 status = odb_can_open(pvfs->odb_context, &key,
1290 NTCREATEX_SHARE_ACCESS_READ |
1291 NTCREATEX_SHARE_ACCESS_WRITE,