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 "system/filesys.h"
27 #include "dlinklist.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 smbsrv_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;
72 if (h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
73 if (rmdir(h->name->full_name) != 0) {
74 DEBUG(0,("pvfs_close: failed to rmdir '%s' - %s\n",
75 h->name->full_name, strerror(errno)));
83 cleanup a open directory fnum
85 static int pvfs_dir_fnum_destructor(void *p)
87 struct pvfs_file *f = p;
88 DLIST_REMOVE(f->pvfs->open_files, f);
89 idr_remove(f->pvfs->idtree_fnum, f->fnum);
97 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
98 struct smbsrv_request *req,
99 struct pvfs_filename *name,
105 uint32_t create_action;
106 uint32_t access_mask = io->generic.in.access_mask;
108 if (name->stream_name) {
109 return NT_STATUS_NOT_A_DIRECTORY;
112 /* if the client says it must be a directory, and it isn't,
114 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
115 return NT_STATUS_NOT_A_DIRECTORY;
118 switch (io->generic.in.open_disposition) {
119 case NTCREATEX_DISP_OPEN_IF:
122 case NTCREATEX_DISP_OPEN:
124 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
128 case NTCREATEX_DISP_CREATE:
130 return NT_STATUS_OBJECT_NAME_COLLISION;
134 case NTCREATEX_DISP_OVERWRITE_IF:
135 case NTCREATEX_DISP_OVERWRITE:
136 case NTCREATEX_DISP_SUPERSEDE:
138 return NT_STATUS_INVALID_PARAMETER;
141 f = talloc_p(req, struct pvfs_file);
143 return NT_STATUS_NO_MEMORY;
146 f->handle = talloc_p(f, struct pvfs_file_handle);
147 if (f->handle == NULL) {
148 return NT_STATUS_NO_MEMORY;
151 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
153 return NT_STATUS_TOO_MANY_OPENED_FILES;
157 /* check the security descriptor */
158 status = pvfs_access_check(pvfs, req, name, &access_mask);
159 if (!NT_STATUS_IS_OK(status)) {
165 f->session = req->session;
166 f->smbpid = req->smbpid;
168 f->pending_list = NULL;
170 f->share_access = io->generic.in.share_access;
171 f->impersonation = io->generic.in.impersonation;
172 f->access_mask = access_mask;
174 f->handle->pvfs = pvfs;
175 f->handle->name = talloc_steal(f->handle, name);
177 f->handle->odb_locking_key = data_blob(NULL, 0);
178 f->handle->brl_locking_key = data_blob(NULL, 0);
179 f->handle->create_options = io->generic.in.create_options;
180 f->handle->seek_offset = 0;
181 f->handle->position = 0;
183 f->handle->sticky_write_time = False;
185 DLIST_ADD(pvfs->open_files, f);
187 /* TODO: should we check in the opendb? Do directory opens
188 follow the share_access rules? */
190 /* setup destructors to avoid leaks on abnormal termination */
191 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
192 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
195 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
196 mode_t mode = pvfs_fileperms(pvfs, attrib);
197 if (mkdir(name->full_name, mode) == -1) {
198 return pvfs_map_errno(pvfs,errno);
200 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
201 if (!NT_STATUS_IS_OK(status)) {
204 create_action = NTCREATEX_ACTION_CREATED;
206 create_action = NTCREATEX_ACTION_EXISTED;
210 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
213 /* the open succeeded, keep this handle permanently */
214 talloc_steal(pvfs, f);
216 io->generic.out.oplock_level = OPLOCK_NONE;
217 io->generic.out.fnum = f->fnum;
218 io->generic.out.create_action = create_action;
219 io->generic.out.create_time = name->dos.create_time;
220 io->generic.out.access_time = name->dos.access_time;
221 io->generic.out.write_time = name->dos.write_time;
222 io->generic.out.change_time = name->dos.change_time;
223 io->generic.out.attrib = name->dos.attrib;
224 io->generic.out.alloc_size = name->dos.alloc_size;
225 io->generic.out.size = name->st.st_size;
226 io->generic.out.file_type = FILE_TYPE_DISK;
227 io->generic.out.ipc_state = 0;
228 io->generic.out.is_directory = 1;
234 destroy a struct pvfs_file_handle
236 static int pvfs_handle_destructor(void *p)
238 struct pvfs_file_handle *h = p;
240 /* the write time is no longer sticky */
241 if (h->sticky_write_time) {
243 status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
244 if (NT_STATUS_IS_OK(status)) {
245 h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
246 pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
250 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
251 h->name->stream_name) {
253 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
254 if (!NT_STATUS_IS_OK(status)) {
255 DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
256 h->name->stream_name, h->name->full_name));
261 if (close(h->fd) != 0) {
262 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
263 h->fd, h->name->full_name, strerror(errno)));
268 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
269 h->name->stream_name == NULL) {
270 if (unlink(h->name->full_name) != 0) {
271 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
272 h->name->full_name, strerror(errno)));
276 if (h->have_opendb_entry) {
277 struct odb_lock *lck;
280 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
282 DEBUG(0,("Unable to lock opendb for close\n"));
286 status = odb_close_file(lck, h);
287 if (!NT_STATUS_IS_OK(status)) {
288 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
289 h->name->full_name, nt_errstr(status)));
300 destroy a struct pvfs_file
302 static int pvfs_fnum_destructor(void *p)
304 struct pvfs_file *f = p;
306 DLIST_REMOVE(f->pvfs->open_files, f);
307 pvfs_lock_close(f->pvfs, f);
308 idr_remove(f->pvfs->idtree_fnum, f->fnum);
315 form the lock context used for opendb locking. Note that we must
316 zero here to take account of possible padding on some architectures
318 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
319 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
325 ZERO_STRUCT(lock_context);
327 lock_context.device = name->st.st_dev;
328 lock_context.inode = name->st.st_ino;
330 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
331 if (key->data == NULL) {
332 return NT_STATUS_NO_MEMORY;
339 form the lock context used for byte range locking. This is separate
340 from the locking key used for opendb locking as it needs to take
341 account of file streams (each stream is a separate byte range
344 static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name,
345 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
349 status = pvfs_locking_key(name, mem_ctx, &odb_key);
350 if (!NT_STATUS_IS_OK(status)) {
353 if (name->stream_name == NULL) {
357 *key = data_blob_talloc(mem_ctx, NULL,
358 odb_key.length + strlen(name->stream_name) + 1);
359 if (key->data == NULL) {
360 return NT_STATUS_NO_MEMORY;
362 memcpy(key->data, odb_key.data, odb_key.length);
363 memcpy(key->data + odb_key.length,
364 name->stream_name, strlen(name->stream_name)+1);
365 data_blob_free(&odb_key);
373 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
374 struct smbsrv_request *req,
375 struct pvfs_filename *name,
381 struct odb_lock *lck;
382 uint32_t create_options = io->generic.in.create_options;
383 uint32_t share_access = io->generic.in.share_access;
384 uint32_t access_mask = io->generic.in.access_mask;
388 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
389 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
390 return NT_STATUS_CANNOT_DELETE;
393 if (access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
394 access_mask = SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_WRITE;
397 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
403 f = talloc_p(req, struct pvfs_file);
405 return NT_STATUS_NO_MEMORY;
408 f->handle = talloc_p(f, struct pvfs_file_handle);
409 if (f->handle == NULL) {
410 return NT_STATUS_NO_MEMORY;
413 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
415 return NT_STATUS_TOO_MANY_OPENED_FILES;
418 attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
419 mode = pvfs_fileperms(pvfs, attrib);
421 /* create the file */
422 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
424 idr_remove(pvfs->idtree_fnum, fnum);
425 return pvfs_map_errno(pvfs, errno);
428 /* if this was a stream create then create the stream as well */
429 if (name->stream_name) {
430 status = pvfs_stream_create(pvfs, name, fd);
431 if (!NT_STATUS_IS_OK(status)) {
432 idr_remove(pvfs->idtree_fnum, fnum);
438 /* re-resolve the open fd */
439 status = pvfs_resolve_name_fd(pvfs, fd, name);
440 if (!NT_STATUS_IS_OK(status)) {
441 idr_remove(pvfs->idtree_fnum, fnum);
446 name->dos.attrib = attrib;
447 status = pvfs_dosattrib_save(pvfs, name, fd);
448 if (!NT_STATUS_IS_OK(status)) {
449 idr_remove(pvfs->idtree_fnum, fnum);
454 /* setup any EAs that were asked for */
455 if (io->ntcreatex.in.ea_list) {
457 for (i=0;i<io->ntcreatex.in.ea_list->num_eas;i++) {
458 status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
459 &io->ntcreatex.in.ea_list->eas[i]);
460 if (!NT_STATUS_IS_OK(status)) {
461 idr_remove(pvfs->idtree_fnum, fnum);
468 /* setup an initial sec_desc is required */
469 if (io->ntcreatex.in.sec_desc) {
470 union smb_setfileinfo set;
472 set.set_secdesc.file.fnum = fnum;
473 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
474 set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
476 status = pvfs_acl_set(pvfs, req, name, fd, &set);
477 if (!NT_STATUS_IS_OK(status)) {
478 idr_remove(pvfs->idtree_fnum, fnum);
484 /* form the lock context used for byte range locking and
486 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
487 if (!NT_STATUS_IS_OK(status)) {
488 idr_remove(pvfs->idtree_fnum, fnum);
493 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
494 if (!NT_STATUS_IS_OK(status)) {
495 idr_remove(pvfs->idtree_fnum, fnum);
500 /* grab a lock on the open file record */
501 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
503 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
505 /* we were supposed to do a blocking lock, so something
507 idr_remove(pvfs->idtree_fnum, fnum);
509 return NT_STATUS_INTERNAL_DB_CORRUPTION;
512 status = odb_open_file(lck, f->handle, name->stream_id,
513 share_access, create_options, access_mask);
515 if (!NT_STATUS_IS_OK(status)) {
516 /* bad news, we must have hit a race */
517 idr_remove(pvfs->idtree_fnum, fnum);
523 f->session = req->session;
524 f->smbpid = req->smbpid;
526 f->pending_list = NULL;
528 f->share_access = io->generic.in.share_access;
529 f->access_mask = access_mask;
530 f->impersonation = io->generic.in.impersonation;
532 f->handle->pvfs = pvfs;
533 f->handle->name = talloc_steal(f->handle, name);
535 f->handle->create_options = io->generic.in.create_options;
536 f->handle->seek_offset = 0;
537 f->handle->position = 0;
539 f->handle->have_opendb_entry = True;
540 f->handle->sticky_write_time = False;
542 DLIST_ADD(pvfs->open_files, f);
544 /* setup a destructor to avoid file descriptor leaks on
545 abnormal termination */
546 talloc_set_destructor(f, pvfs_fnum_destructor);
547 talloc_set_destructor(f->handle, pvfs_handle_destructor);
550 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
551 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
553 io->generic.out.oplock_level = OPLOCK_NONE;
555 io->generic.out.fnum = f->fnum;
556 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
557 io->generic.out.create_time = name->dos.create_time;
558 io->generic.out.access_time = name->dos.access_time;
559 io->generic.out.write_time = name->dos.write_time;
560 io->generic.out.change_time = name->dos.change_time;
561 io->generic.out.attrib = name->dos.attrib;
562 io->generic.out.alloc_size = name->dos.alloc_size;
563 io->generic.out.size = name->st.st_size;
564 io->generic.out.file_type = FILE_TYPE_DISK;
565 io->generic.out.ipc_state = 0;
566 io->generic.out.is_directory = 0;
568 /* success - keep the file handle */
569 talloc_steal(pvfs, f);
576 state of a pending open retry
578 struct pvfs_open_retry {
579 struct ntvfs_module_context *ntvfs;
580 struct smbsrv_request *req;
583 DATA_BLOB odb_locking_key;
586 /* destroy a pending open request */
587 static int pvfs_retry_destructor(void *ptr)
589 struct pvfs_open_retry *r = ptr;
590 struct pvfs_state *pvfs = r->ntvfs->private_data;
591 if (r->odb_locking_key.data) {
592 struct odb_lock *lck;
593 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
595 odb_remove_pending(lck, r);
605 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
607 struct pvfs_open_retry *r = private;
608 struct ntvfs_module_context *ntvfs = r->ntvfs;
609 struct smbsrv_request *req = r->req;
610 union smb_open *io = r->io;
613 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
614 just a bug in their server, but we better do the same */
615 if (reason == PVFS_WAIT_CANCEL) {
619 talloc_free(r->wait_handle);
621 if (reason == PVFS_WAIT_TIMEOUT) {
622 /* if it timed out, then give the failure
625 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
626 req->async_states->send_fn(req);
630 /* the pending odb entry is already removed. We use a null locking
631 key to indicate this */
632 data_blob_free(&r->odb_locking_key);
635 /* try the open again, which could trigger another retry setup
636 if it wants to, so we have to unmark the async flag so we
637 will know if it does a second async reply */
638 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
640 status = pvfs_open(ntvfs, req, io);
641 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
642 /* the 2nd try also replied async, so we don't send
647 /* re-mark it async, just in case someone up the chain does
649 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
651 /* send the reply up the chain */
652 req->async_states->status = status;
653 req->async_states->send_fn(req);
658 special handling for openx DENY_DOS semantics
660 This function attempts a reference open using an existing handle. If its allowed,
661 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
662 open processing continues.
664 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
665 struct smbsrv_request *req, union smb_open *io,
666 struct pvfs_file *f, struct odb_lock *lck)
668 struct pvfs_state *pvfs = ntvfs->private_data;
669 struct pvfs_file *f2;
670 struct pvfs_filename *name;
672 /* search for an existing open with the right parameters. Note
673 the magic ntcreatex options flag, which is set in the
674 generic mapping code. This might look ugly, but its
675 actually pretty much now w2k does it internally as well.
677 If you look at the BASE-DENYDOS test you will see that a
678 DENY_DOS is a very special case, and in the right
679 circumstances you actually get the _same_ handle back
680 twice, rather than a new handle.
682 for (f2=pvfs->open_files;f2;f2=f2->next) {
684 f2->session == req->session &&
685 f2->smbpid == req->smbpid &&
686 (f2->handle->create_options &
687 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
688 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
689 (f2->access_mask & SEC_FILE_WRITE_DATA) &&
690 StrCaseCmp(f2->handle->name->original_name,
691 io->generic.in.fname)==0) {
697 return NT_STATUS_SHARING_VIOLATION;
700 /* quite an insane set of semantics ... */
701 if (is_exe_filename(io->generic.in.fname) &&
702 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
703 return NT_STATUS_SHARING_VIOLATION;
707 setup a reference to the existing handle
709 talloc_free(f->handle);
710 f->handle = talloc_reference(f, f2->handle);
714 name = f->handle->name;
716 io->generic.out.oplock_level = OPLOCK_NONE;
717 io->generic.out.fnum = f->fnum;
718 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
719 io->generic.out.create_time = name->dos.create_time;
720 io->generic.out.access_time = name->dos.access_time;
721 io->generic.out.write_time = name->dos.write_time;
722 io->generic.out.change_time = name->dos.change_time;
723 io->generic.out.attrib = name->dos.attrib;
724 io->generic.out.alloc_size = name->dos.alloc_size;
725 io->generic.out.size = name->st.st_size;
726 io->generic.out.file_type = FILE_TYPE_DISK;
727 io->generic.out.ipc_state = 0;
728 io->generic.out.is_directory = 0;
730 talloc_steal(f->pvfs, f);
738 setup for a open retry after a sharing violation
740 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
741 struct smbsrv_request *req,
744 struct odb_lock *lck)
746 struct pvfs_state *pvfs = ntvfs->private_data;
747 struct pvfs_open_retry *r;
749 struct timeval end_time;
751 if (io->generic.in.create_options &
752 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
753 /* see if we can satisfy the request using the special DENY_DOS
755 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
756 if (NT_STATUS_IS_OK(status)) {
761 r = talloc_p(req, struct pvfs_open_retry);
763 return NT_STATUS_NO_MEMORY;
769 r->odb_locking_key = data_blob_talloc(r,
770 f->handle->odb_locking_key.data,
771 f->handle->odb_locking_key.length);
773 end_time = timeval_add(&req->request_time, 0, pvfs->sharing_violation_delay);
775 /* setup a pending lock */
776 status = odb_open_file_pending(lck, r);
777 if (!NT_STATUS_IS_OK(status)) {
784 talloc_set_destructor(r, pvfs_retry_destructor);
786 r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
788 if (r->wait_handle == NULL) {
789 return NT_STATUS_NO_MEMORY;
792 talloc_steal(pvfs, r);
798 special handling for t2open
800 static NTSTATUS pvfs_open_t2open(struct ntvfs_module_context *ntvfs,
801 struct smbsrv_request *req, union smb_open *io)
803 struct pvfs_state *pvfs = ntvfs->private_data;
804 struct pvfs_filename *name;
807 status = pvfs_resolve_name(pvfs, req, io->t2open.in.fname, 0, &name);
808 if (!NT_STATUS_IS_OK(status)) {
812 if (io->t2open.in.open_func & OPENX_OPEN_FUNC_CREATE) {
813 if (!name->stream_exists) return NT_STATUS_ACCESS_DENIED;
815 if (io->t2open.in.open_func & OPENX_OPEN_FUNC_TRUNC) {
816 if (name->stream_exists) return NT_STATUS_ACCESS_DENIED;
817 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
819 if ((io->t2open.in.open_func & 0xF) == OPENX_OPEN_FUNC_FAIL) {
820 if (!name->stream_exists) return NT_STATUS_ACCESS_DENIED;
821 return NT_STATUS_OBJECT_NAME_COLLISION;
826 return ntvfs_map_open(req, io, ntvfs);
832 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
833 struct smbsrv_request *req, union smb_open *io)
835 struct pvfs_state *pvfs = ntvfs->private_data;
837 struct pvfs_filename *name;
841 struct odb_lock *lck;
842 uint32_t create_options;
843 uint32_t share_access;
844 uint32_t access_mask;
847 if (io->generic.level == RAW_OPEN_T2OPEN) {
848 return pvfs_open_t2open(ntvfs, req, io);
851 /* use the generic mapping code to avoid implementing all the
852 different open calls. */
853 if (io->generic.level != RAW_OPEN_GENERIC &&
854 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
855 return ntvfs_map_open(req, io, ntvfs);
858 /* resolve the cifs name to a posix name */
859 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
860 PVFS_RESOLVE_STREAMS, &name);
861 if (!NT_STATUS_IS_OK(status)) {
865 /* directory opens are handled separately */
866 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
867 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
868 return pvfs_open_directory(pvfs, req, name, io);
871 create_options = io->generic.in.create_options;
872 share_access = io->generic.in.share_access;
873 access_mask = io->generic.in.access_mask;
875 /* certain create options are not allowed */
876 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
877 !(access_mask & SEC_STD_DELETE)) {
878 return NT_STATUS_INVALID_PARAMETER;
881 switch (io->generic.in.open_disposition) {
882 case NTCREATEX_DISP_SUPERSEDE:
886 case NTCREATEX_DISP_OVERWRITE_IF:
890 case NTCREATEX_DISP_OPEN:
891 if (!name->stream_exists) {
892 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
897 case NTCREATEX_DISP_OVERWRITE:
898 if (!name->stream_exists) {
899 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
904 case NTCREATEX_DISP_CREATE:
905 if (name->stream_exists) {
906 return NT_STATUS_OBJECT_NAME_COLLISION;
911 case NTCREATEX_DISP_OPEN_IF:
916 return NT_STATUS_INVALID_PARAMETER;
919 if (io->generic.in.file_attr & FILE_ATTRIBUTE_DIRECTORY) {
920 return NT_STATUS_INVALID_PARAMETER;
923 /* handle creating a new file separately */
925 status = pvfs_create_file(pvfs, req, name, io);
926 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
930 /* we've hit a race - the file was created during this call */
931 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
935 /* try re-resolving the name */
936 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
937 if (!NT_STATUS_IS_OK(status)) {
940 /* fall through to a normal open */
943 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
944 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
945 return NT_STATUS_CANNOT_DELETE;
948 /* check the security descriptor */
949 status = pvfs_access_check(pvfs, req, name, &access_mask);
950 if (!NT_STATUS_IS_OK(status)) {
954 f = talloc_p(req, struct pvfs_file);
956 return NT_STATUS_NO_MEMORY;
959 f->handle = talloc_p(f, struct pvfs_file_handle);
960 if (f->handle == NULL) {
961 return NT_STATUS_NO_MEMORY;
964 /* allocate a fnum */
965 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
967 return NT_STATUS_TOO_MANY_OPENED_FILES;
971 f->session = req->session;
972 f->smbpid = req->smbpid;
974 f->pending_list = NULL;
976 f->share_access = io->generic.in.share_access;
977 f->access_mask = access_mask;
978 f->impersonation = io->generic.in.impersonation;
980 f->handle->pvfs = pvfs;
982 f->handle->name = talloc_steal(f->handle, name);
983 f->handle->create_options = io->generic.in.create_options;
984 f->handle->seek_offset = 0;
985 f->handle->position = 0;
987 f->handle->have_opendb_entry = False;
988 f->handle->sticky_write_time = False;
990 /* form the lock context used for byte range locking and
992 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
993 if (!NT_STATUS_IS_OK(status)) {
994 idr_remove(pvfs->idtree_fnum, f->fnum);
998 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
999 if (!NT_STATUS_IS_OK(status)) {
1000 idr_remove(pvfs->idtree_fnum, f->fnum);
1004 /* get a lock on this file before the actual open */
1005 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1007 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1009 /* we were supposed to do a blocking lock, so something
1011 idr_remove(pvfs->idtree_fnum, fnum);
1012 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1015 DLIST_ADD(pvfs->open_files, f);
1017 /* setup a destructor to avoid file descriptor leaks on
1018 abnormal termination */
1019 talloc_set_destructor(f, pvfs_fnum_destructor);
1020 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1023 /* see if we are allowed to open at the same time as existing opens */
1024 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
1025 share_access, create_options, access_mask);
1027 /* on a sharing violation we need to retry when the file is closed by
1028 the other user, or after 1 second */
1029 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1030 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1031 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1034 if (!NT_STATUS_IS_OK(status)) {
1039 f->handle->have_opendb_entry = True;
1041 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1047 /* do the actual open */
1048 fd = open(f->handle->name->full_name, flags);
1051 return pvfs_map_errno(f->pvfs, errno);
1056 stream_existed = name->stream_exists;
1058 /* if this was a stream create then create the stream as well */
1059 if (!name->stream_exists) {
1060 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1061 if (!NT_STATUS_IS_OK(status)) {
1067 /* re-resolve the open fd */
1068 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1069 if (!NT_STATUS_IS_OK(status)) {
1074 if (f->handle->name->stream_id == 0 &&
1075 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1076 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1077 /* for overwrite we need to replace file permissions */
1078 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1079 mode_t mode = pvfs_fileperms(pvfs, attrib);
1080 if (fchmod(fd, mode) == -1) {
1082 return pvfs_map_errno(pvfs, errno);
1084 name->dos.attrib = attrib;
1085 status = pvfs_dosattrib_save(pvfs, name, fd);
1086 if (!NT_STATUS_IS_OK(status)) {
1094 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1095 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
1097 io->generic.out.oplock_level = OPLOCK_NONE;
1099 io->generic.out.fnum = f->fnum;
1100 io->generic.out.create_action = stream_existed?
1101 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1102 io->generic.out.create_time = name->dos.create_time;
1103 io->generic.out.access_time = name->dos.access_time;
1104 io->generic.out.write_time = name->dos.write_time;
1105 io->generic.out.change_time = name->dos.change_time;
1106 io->generic.out.attrib = name->dos.attrib;
1107 io->generic.out.alloc_size = name->dos.alloc_size;
1108 io->generic.out.size = name->st.st_size;
1109 io->generic.out.file_type = FILE_TYPE_DISK;
1110 io->generic.out.ipc_state = 0;
1111 io->generic.out.is_directory = 0;
1113 /* success - keep the file handle */
1114 talloc_steal(f->pvfs, f);
1116 return NT_STATUS_OK;
1123 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1124 struct smbsrv_request *req, union smb_close *io)
1126 struct pvfs_state *pvfs = ntvfs->private_data;
1127 struct pvfs_file *f;
1128 struct utimbuf unix_times;
1130 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1131 return NT_STATUS_UNSUCCESSFUL;
1134 if (io->generic.level != RAW_CLOSE_CLOSE) {
1135 return ntvfs_map_close(req, io, ntvfs);
1138 f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
1140 return NT_STATUS_INVALID_HANDLE;
1143 if (!null_time(io->close.in.write_time)) {
1144 unix_times.actime = 0;
1145 unix_times.modtime = io->close.in.write_time;
1146 utime(f->handle->name->full_name, &unix_times);
1147 } else if (f->handle->sticky_write_time) {
1148 unix_times.actime = 0;
1149 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1150 utime(f->handle->name->full_name, &unix_times);
1155 return NT_STATUS_OK;
1160 logoff - close all file descriptors open by a vuid
1162 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1163 struct smbsrv_request *req)
1165 struct pvfs_state *pvfs = ntvfs->private_data;
1166 struct pvfs_file *f, *next;
1168 for (f=pvfs->open_files;f;f=next) {
1170 if (f->session == req->session) {
1175 return NT_STATUS_OK;
1180 exit - close files for the current pid
1182 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1183 struct smbsrv_request *req)
1185 struct pvfs_state *pvfs = ntvfs->private_data;
1186 struct pvfs_file *f, *next;
1188 for (f=pvfs->open_files;f;f=next) {
1190 if (f->smbpid == req->smbpid) {
1195 return NT_STATUS_OK;
1200 change the create options on an already open file
1202 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
1203 struct smbsrv_request *req,
1204 struct pvfs_file *f, uint32_t create_options)
1206 struct odb_lock *lck;
1209 if (f->handle->create_options == create_options) {
1210 return NT_STATUS_OK;
1213 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1214 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1215 return NT_STATUS_CANNOT_DELETE;
1218 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1220 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1223 status = odb_set_create_options(lck, f->handle, create_options);
1224 if (NT_STATUS_IS_OK(status)) {
1225 f->handle->create_options = create_options;
1233 determine if a file can be deleted, or if it is prevented by an
1236 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs, struct pvfs_filename *name)
1241 status = pvfs_locking_key(name, name, &key);
1242 if (!NT_STATUS_IS_OK(status)) {
1243 return NT_STATUS_NO_MEMORY;
1246 status = odb_can_open(pvfs->odb_context, &key,
1247 NTCREATEX_SHARE_ACCESS_READ |
1248 NTCREATEX_SHARE_ACCESS_WRITE |
1249 NTCREATEX_SHARE_ACCESS_DELETE,
1250 NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
1257 determine if a file can be renamed, or if it is prevented by an
1260 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs, struct pvfs_filename *name)
1265 status = pvfs_locking_key(name, name, &key);
1266 if (!NT_STATUS_IS_OK(status)) {
1267 return NT_STATUS_NO_MEMORY;
1270 status = odb_can_open(pvfs->odb_context, &key,
1271 NTCREATEX_SHARE_ACCESS_READ |
1272 NTCREATEX_SHARE_ACCESS_WRITE,