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;
107 if (name->stream_name) {
108 return NT_STATUS_NOT_A_DIRECTORY;
111 /* if the client says it must be a directory, and it isn't,
113 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
114 return NT_STATUS_NOT_A_DIRECTORY;
117 switch (io->generic.in.open_disposition) {
118 case NTCREATEX_DISP_OPEN_IF:
121 case NTCREATEX_DISP_OPEN:
123 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
127 case NTCREATEX_DISP_CREATE:
129 return NT_STATUS_OBJECT_NAME_COLLISION;
133 case NTCREATEX_DISP_OVERWRITE_IF:
134 case NTCREATEX_DISP_OVERWRITE:
135 case NTCREATEX_DISP_SUPERSEDE:
137 return NT_STATUS_INVALID_PARAMETER;
140 f = talloc_p(req, struct pvfs_file);
142 return NT_STATUS_NO_MEMORY;
145 f->handle = talloc_p(f, struct pvfs_file_handle);
146 if (f->handle == NULL) {
147 return NT_STATUS_NO_MEMORY;
150 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
152 return NT_STATUS_TOO_MANY_OPENED_FILES;
156 f->session = req->session;
157 f->smbpid = req->smbpid;
159 f->pending_list = NULL;
161 f->share_access = io->generic.in.share_access;
162 f->impersonation = io->generic.in.impersonation;
164 f->handle->pvfs = pvfs;
165 f->handle->name = talloc_steal(f->handle, name);
167 f->handle->odb_locking_key = data_blob(NULL, 0);
168 f->handle->brl_locking_key = data_blob(NULL, 0);
169 f->handle->create_options = io->generic.in.create_options;
170 f->handle->seek_offset = 0;
171 f->handle->position = 0;
173 f->handle->sticky_write_time = False;
175 DLIST_ADD(pvfs->open_files, f);
177 /* TODO: should we check in the opendb? Do directory opens
178 follow the share_access rules? */
180 /* setup destructors to avoid leaks on abnormal termination */
181 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
182 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
185 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
186 mode_t mode = pvfs_fileperms(pvfs, attrib);
187 if (mkdir(name->full_name, mode) == -1) {
188 return pvfs_map_errno(pvfs,errno);
190 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
191 if (!NT_STATUS_IS_OK(status)) {
194 create_action = NTCREATEX_ACTION_CREATED;
196 create_action = NTCREATEX_ACTION_EXISTED;
200 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
203 /* the open succeeded, keep this handle permanently */
204 talloc_steal(pvfs, f);
206 io->generic.out.oplock_level = OPLOCK_NONE;
207 io->generic.out.fnum = f->fnum;
208 io->generic.out.create_action = create_action;
209 io->generic.out.create_time = name->dos.create_time;
210 io->generic.out.access_time = name->dos.access_time;
211 io->generic.out.write_time = name->dos.write_time;
212 io->generic.out.change_time = name->dos.change_time;
213 io->generic.out.attrib = name->dos.attrib;
214 io->generic.out.alloc_size = name->dos.alloc_size;
215 io->generic.out.size = name->st.st_size;
216 io->generic.out.file_type = FILE_TYPE_DISK;
217 io->generic.out.ipc_state = 0;
218 io->generic.out.is_directory = 1;
224 destroy a struct pvfs_file_handle
226 static int pvfs_handle_destructor(void *p)
228 struct pvfs_file_handle *h = p;
230 /* the write time is no longer sticky */
231 if (h->sticky_write_time) {
233 status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
234 if (NT_STATUS_IS_OK(status)) {
235 h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
236 pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
240 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
241 h->name->stream_name) {
243 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
244 if (!NT_STATUS_IS_OK(status)) {
245 DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
246 h->name->stream_name, h->name->full_name));
251 if (close(h->fd) != 0) {
252 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
253 h->fd, h->name->full_name, strerror(errno)));
258 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
259 h->name->stream_name == NULL) {
260 if (unlink(h->name->full_name) != 0) {
261 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
262 h->name->full_name, strerror(errno)));
266 if (h->have_opendb_entry) {
267 struct odb_lock *lck;
270 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
272 DEBUG(0,("Unable to lock opendb for close\n"));
276 status = odb_close_file(lck, h);
277 if (!NT_STATUS_IS_OK(status)) {
278 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
279 h->name->full_name, nt_errstr(status)));
290 destroy a struct pvfs_file
292 static int pvfs_fnum_destructor(void *p)
294 struct pvfs_file *f = p;
296 DLIST_REMOVE(f->pvfs->open_files, f);
297 pvfs_lock_close(f->pvfs, f);
298 idr_remove(f->pvfs->idtree_fnum, f->fnum);
305 form the lock context used for opendb locking. Note that we must
306 zero here to take account of possible padding on some architectures
308 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
309 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
315 ZERO_STRUCT(lock_context);
317 lock_context.device = name->st.st_dev;
318 lock_context.inode = name->st.st_ino;
320 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
321 if (key->data == NULL) {
322 return NT_STATUS_NO_MEMORY;
329 form the lock context used for byte range locking. This is separate
330 from the locking key used for opendb locking as it needs to take
331 account of file streams (each stream is a separate byte range
334 static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name,
335 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
339 status = pvfs_locking_key(name, mem_ctx, &odb_key);
340 if (!NT_STATUS_IS_OK(status)) {
343 if (name->stream_name == NULL) {
347 *key = data_blob_talloc(mem_ctx, NULL,
348 odb_key.length + strlen(name->stream_name) + 1);
349 if (key->data == NULL) {
350 return NT_STATUS_NO_MEMORY;
352 memcpy(key->data, odb_key.data, odb_key.length);
353 memcpy(key->data + odb_key.length,
354 name->stream_name, strlen(name->stream_name)+1);
355 data_blob_free(&odb_key);
363 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
364 struct smbsrv_request *req,
365 struct pvfs_filename *name,
371 struct odb_lock *lck;
372 uint32_t create_options = io->generic.in.create_options;
373 uint32_t share_access = io->generic.in.share_access;
374 uint32_t access_mask = io->generic.in.access_mask;
378 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
379 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
380 return NT_STATUS_CANNOT_DELETE;
383 if (access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
384 access_mask = SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_WRITE;
387 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
393 f = talloc_p(req, struct pvfs_file);
395 return NT_STATUS_NO_MEMORY;
398 f->handle = talloc_p(f, struct pvfs_file_handle);
399 if (f->handle == NULL) {
400 return NT_STATUS_NO_MEMORY;
403 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
405 return NT_STATUS_TOO_MANY_OPENED_FILES;
408 attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
409 mode = pvfs_fileperms(pvfs, attrib);
411 /* create the file */
412 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
414 idr_remove(pvfs->idtree_fnum, fnum);
415 return pvfs_map_errno(pvfs, errno);
418 /* if this was a stream create then create the stream as well */
419 if (name->stream_name) {
420 status = pvfs_stream_create(pvfs, name, fd);
421 if (!NT_STATUS_IS_OK(status)) {
422 idr_remove(pvfs->idtree_fnum, fnum);
428 /* re-resolve the open fd */
429 status = pvfs_resolve_name_fd(pvfs, fd, name);
430 if (!NT_STATUS_IS_OK(status)) {
431 idr_remove(pvfs->idtree_fnum, fnum);
436 name->dos.attrib = attrib;
437 status = pvfs_dosattrib_save(pvfs, name, fd);
438 if (!NT_STATUS_IS_OK(status)) {
439 idr_remove(pvfs->idtree_fnum, fnum);
444 /* setup any EAs that were asked for */
445 if (io->ntcreatex.in.ea_list) {
447 for (i=0;i<io->ntcreatex.in.ea_list->num_eas;i++) {
448 status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
449 &io->ntcreatex.in.ea_list->eas[i]);
450 if (!NT_STATUS_IS_OK(status)) {
451 idr_remove(pvfs->idtree_fnum, fnum);
458 /* setup an initial sec_desc is required */
459 if (io->ntcreatex.in.sec_desc) {
460 union smb_setfileinfo set;
462 set.set_secdesc.file.fnum = fnum;
463 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
464 set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
466 status = pvfs_acl_set(pvfs, req, name, fd, &set);
467 if (!NT_STATUS_IS_OK(status)) {
468 idr_remove(pvfs->idtree_fnum, fnum);
474 /* form the lock context used for byte range locking and
476 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
477 if (!NT_STATUS_IS_OK(status)) {
478 idr_remove(pvfs->idtree_fnum, fnum);
483 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
484 if (!NT_STATUS_IS_OK(status)) {
485 idr_remove(pvfs->idtree_fnum, fnum);
490 /* grab a lock on the open file record */
491 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
493 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
495 /* we were supposed to do a blocking lock, so something
497 idr_remove(pvfs->idtree_fnum, fnum);
499 return NT_STATUS_INTERNAL_DB_CORRUPTION;
502 status = odb_open_file(lck, f->handle, name->stream_id,
503 share_access, create_options, access_mask);
505 if (!NT_STATUS_IS_OK(status)) {
506 /* bad news, we must have hit a race */
507 idr_remove(pvfs->idtree_fnum, fnum);
513 f->session = req->session;
514 f->smbpid = req->smbpid;
516 f->pending_list = NULL;
518 f->share_access = io->generic.in.share_access;
519 f->access_mask = access_mask;
520 f->impersonation = io->generic.in.impersonation;
522 f->handle->pvfs = pvfs;
523 f->handle->name = talloc_steal(f->handle, name);
525 f->handle->create_options = io->generic.in.create_options;
526 f->handle->seek_offset = 0;
527 f->handle->position = 0;
529 f->handle->have_opendb_entry = True;
530 f->handle->sticky_write_time = False;
532 DLIST_ADD(pvfs->open_files, f);
534 /* setup a destructor to avoid file descriptor leaks on
535 abnormal termination */
536 talloc_set_destructor(f, pvfs_fnum_destructor);
537 talloc_set_destructor(f->handle, pvfs_handle_destructor);
540 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
541 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
543 io->generic.out.oplock_level = OPLOCK_NONE;
545 io->generic.out.fnum = f->fnum;
546 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
547 io->generic.out.create_time = name->dos.create_time;
548 io->generic.out.access_time = name->dos.access_time;
549 io->generic.out.write_time = name->dos.write_time;
550 io->generic.out.change_time = name->dos.change_time;
551 io->generic.out.attrib = name->dos.attrib;
552 io->generic.out.alloc_size = name->dos.alloc_size;
553 io->generic.out.size = name->st.st_size;
554 io->generic.out.file_type = FILE_TYPE_DISK;
555 io->generic.out.ipc_state = 0;
556 io->generic.out.is_directory = 0;
558 /* success - keep the file handle */
559 talloc_steal(pvfs, f);
566 state of a pending open retry
568 struct pvfs_open_retry {
569 struct ntvfs_module_context *ntvfs;
570 struct smbsrv_request *req;
573 DATA_BLOB odb_locking_key;
576 /* destroy a pending open request */
577 static int pvfs_retry_destructor(void *ptr)
579 struct pvfs_open_retry *r = ptr;
580 struct pvfs_state *pvfs = r->ntvfs->private_data;
581 if (r->odb_locking_key.data) {
582 struct odb_lock *lck;
583 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
585 odb_remove_pending(lck, r);
595 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
597 struct pvfs_open_retry *r = private;
598 struct ntvfs_module_context *ntvfs = r->ntvfs;
599 struct smbsrv_request *req = r->req;
600 union smb_open *io = r->io;
603 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
604 just a bug in their server, but we better do the same */
605 if (reason == PVFS_WAIT_CANCEL) {
609 talloc_free(r->wait_handle);
611 if (reason == PVFS_WAIT_TIMEOUT) {
612 /* if it timed out, then give the failure
615 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
616 req->async_states->send_fn(req);
620 /* the pending odb entry is already removed. We use a null locking
621 key to indicate this */
622 data_blob_free(&r->odb_locking_key);
625 /* try the open again, which could trigger another retry setup
626 if it wants to, so we have to unmark the async flag so we
627 will know if it does a second async reply */
628 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
630 status = pvfs_open(ntvfs, req, io);
631 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
632 /* the 2nd try also replied async, so we don't send
637 /* re-mark it async, just in case someone up the chain does
639 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
641 /* send the reply up the chain */
642 req->async_states->status = status;
643 req->async_states->send_fn(req);
648 special handling for openx DENY_DOS semantics
650 This function attempts a reference open using an existing handle. If its allowed,
651 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
652 open processing continues.
654 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
655 struct smbsrv_request *req, union smb_open *io,
656 struct pvfs_file *f, struct odb_lock *lck)
658 struct pvfs_state *pvfs = ntvfs->private_data;
659 struct pvfs_file *f2;
660 struct pvfs_filename *name;
662 /* search for an existing open with the right parameters. Note
663 the magic ntcreatex options flag, which is set in the
664 generic mapping code. This might look ugly, but its
665 actually pretty much now w2k does it internally as well.
667 If you look at the BASE-DENYDOS test you will see that a
668 DENY_DOS is a very special case, and in the right
669 circumstances you actually get the _same_ handle back
670 twice, rather than a new handle.
672 for (f2=pvfs->open_files;f2;f2=f2->next) {
674 f2->session == req->session &&
675 f2->smbpid == req->smbpid &&
676 (f2->handle->create_options &
677 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
678 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
679 (f2->access_mask & SEC_FILE_WRITE_DATA) &&
680 StrCaseCmp(f2->handle->name->original_name,
681 io->generic.in.fname)==0) {
687 return NT_STATUS_SHARING_VIOLATION;
690 /* quite an insane set of semantics ... */
691 if (is_exe_filename(io->generic.in.fname) &&
692 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
693 return NT_STATUS_SHARING_VIOLATION;
697 setup a reference to the existing handle
699 talloc_free(f->handle);
700 f->handle = talloc_reference(f, f2->handle);
704 name = f->handle->name;
706 io->generic.out.oplock_level = OPLOCK_NONE;
707 io->generic.out.fnum = f->fnum;
708 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
709 io->generic.out.create_time = name->dos.create_time;
710 io->generic.out.access_time = name->dos.access_time;
711 io->generic.out.write_time = name->dos.write_time;
712 io->generic.out.change_time = name->dos.change_time;
713 io->generic.out.attrib = name->dos.attrib;
714 io->generic.out.alloc_size = name->dos.alloc_size;
715 io->generic.out.size = name->st.st_size;
716 io->generic.out.file_type = FILE_TYPE_DISK;
717 io->generic.out.ipc_state = 0;
718 io->generic.out.is_directory = 0;
720 talloc_steal(f->pvfs, f);
728 setup for a open retry after a sharing violation
730 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
731 struct smbsrv_request *req,
734 struct odb_lock *lck)
736 struct pvfs_state *pvfs = ntvfs->private_data;
737 struct pvfs_open_retry *r;
739 struct timeval end_time;
741 if (io->generic.in.create_options &
742 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
743 /* see if we can satisfy the request using the special DENY_DOS
745 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
746 if (NT_STATUS_IS_OK(status)) {
751 r = talloc_p(req, struct pvfs_open_retry);
753 return NT_STATUS_NO_MEMORY;
759 r->odb_locking_key = data_blob_talloc(r,
760 f->handle->odb_locking_key.data,
761 f->handle->odb_locking_key.length);
763 end_time = timeval_add(&req->request_time, 0, pvfs->sharing_violation_delay);
765 /* setup a pending lock */
766 status = odb_open_file_pending(lck, r);
767 if (!NT_STATUS_IS_OK(status)) {
774 talloc_set_destructor(r, pvfs_retry_destructor);
776 r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
778 if (r->wait_handle == NULL) {
779 return NT_STATUS_NO_MEMORY;
782 talloc_steal(pvfs, r);
788 special handling for t2open
790 static NTSTATUS pvfs_open_t2open(struct ntvfs_module_context *ntvfs,
791 struct smbsrv_request *req, union smb_open *io)
793 struct pvfs_state *pvfs = ntvfs->private_data;
794 struct pvfs_filename *name;
797 status = pvfs_resolve_name(pvfs, req, io->t2open.in.fname, 0, &name);
798 if (!NT_STATUS_IS_OK(status)) {
802 if (io->t2open.in.open_func & OPENX_OPEN_FUNC_CREATE) {
803 if (!name->stream_exists) return NT_STATUS_ACCESS_DENIED;
805 if (io->t2open.in.open_func & OPENX_OPEN_FUNC_TRUNC) {
806 if (name->stream_exists) return NT_STATUS_ACCESS_DENIED;
807 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
809 if ((io->t2open.in.open_func & 0xF) == OPENX_OPEN_FUNC_FAIL) {
810 if (!name->stream_exists) return NT_STATUS_ACCESS_DENIED;
811 return NT_STATUS_OBJECT_NAME_COLLISION;
816 return ntvfs_map_open(req, io, ntvfs);
822 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
823 struct smbsrv_request *req, union smb_open *io)
825 struct pvfs_state *pvfs = ntvfs->private_data;
827 struct pvfs_filename *name;
831 struct odb_lock *lck;
832 uint32_t create_options;
833 uint32_t share_access;
834 uint32_t access_mask;
837 if (io->generic.level == RAW_OPEN_T2OPEN) {
838 return pvfs_open_t2open(ntvfs, req, io);
841 /* use the generic mapping code to avoid implementing all the
842 different open calls. */
843 if (io->generic.level != RAW_OPEN_GENERIC &&
844 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
845 return ntvfs_map_open(req, io, ntvfs);
848 /* resolve the cifs name to a posix name */
849 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
850 PVFS_RESOLVE_STREAMS, &name);
851 if (!NT_STATUS_IS_OK(status)) {
855 /* directory opens are handled separately */
856 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
857 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
858 return pvfs_open_directory(pvfs, req, name, io);
861 create_options = io->generic.in.create_options;
862 share_access = io->generic.in.share_access;
863 access_mask = io->generic.in.access_mask;
865 if (access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
866 if (name->exists && (name->dos.attrib & FILE_ATTRIBUTE_READONLY)) {
867 access_mask = SEC_RIGHTS_FILE_READ;
869 access_mask = SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_WRITE;
873 /* certain create options are not allowed */
874 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
875 !(access_mask & SEC_STD_DELETE)) {
876 return NT_STATUS_INVALID_PARAMETER;
879 switch (io->generic.in.open_disposition) {
880 case NTCREATEX_DISP_SUPERSEDE:
884 case NTCREATEX_DISP_OVERWRITE_IF:
888 case NTCREATEX_DISP_OPEN:
889 if (!name->stream_exists) {
890 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
895 case NTCREATEX_DISP_OVERWRITE:
896 if (!name->stream_exists) {
897 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
902 case NTCREATEX_DISP_CREATE:
903 if (name->stream_exists) {
904 return NT_STATUS_OBJECT_NAME_COLLISION;
909 case NTCREATEX_DISP_OPEN_IF:
914 return NT_STATUS_INVALID_PARAMETER;
917 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
923 if (io->generic.in.file_attr & FILE_ATTRIBUTE_DIRECTORY) {
924 return NT_STATUS_INVALID_PARAMETER;
927 /* handle creating a new file separately */
929 status = pvfs_create_file(pvfs, req, name, io);
930 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
934 /* we've hit a race - the file was created during this call */
935 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
939 /* try re-resolving the name */
940 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
941 if (!NT_STATUS_IS_OK(status)) {
944 /* fall through to a normal open */
947 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
948 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
949 return NT_STATUS_CANNOT_DELETE;
952 f = talloc_p(req, struct pvfs_file);
954 return NT_STATUS_NO_MEMORY;
957 f->handle = talloc_p(f, struct pvfs_file_handle);
958 if (f->handle == NULL) {
959 return NT_STATUS_NO_MEMORY;
962 /* allocate a fnum */
963 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
965 return NT_STATUS_TOO_MANY_OPENED_FILES;
969 f->session = req->session;
970 f->smbpid = req->smbpid;
972 f->pending_list = NULL;
974 f->share_access = io->generic.in.share_access;
975 f->access_mask = access_mask;
976 f->impersonation = io->generic.in.impersonation;
978 f->handle->pvfs = pvfs;
980 f->handle->name = talloc_steal(f->handle, name);
981 f->handle->create_options = io->generic.in.create_options;
982 f->handle->seek_offset = 0;
983 f->handle->position = 0;
985 f->handle->have_opendb_entry = False;
986 f->handle->sticky_write_time = False;
988 /* form the lock context used for byte range locking and
990 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
991 if (!NT_STATUS_IS_OK(status)) {
992 idr_remove(pvfs->idtree_fnum, f->fnum);
996 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
997 if (!NT_STATUS_IS_OK(status)) {
998 idr_remove(pvfs->idtree_fnum, f->fnum);
1002 /* get a lock on this file before the actual open */
1003 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1005 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1007 /* we were supposed to do a blocking lock, so something
1009 idr_remove(pvfs->idtree_fnum, fnum);
1010 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1013 DLIST_ADD(pvfs->open_files, f);
1015 /* setup a destructor to avoid file descriptor leaks on
1016 abnormal termination */
1017 talloc_set_destructor(f, pvfs_fnum_destructor);
1018 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1021 /* see if we are allowed to open at the same time as existing opens */
1022 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
1023 share_access, create_options, access_mask);
1025 /* on a sharing violation we need to retry when the file is closed by
1026 the other user, or after 1 second */
1027 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1028 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1029 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1032 if (!NT_STATUS_IS_OK(status)) {
1037 f->handle->have_opendb_entry = True;
1039 /* do the actual open */
1040 fd = open(f->handle->name->full_name, flags);
1043 return pvfs_map_errno(f->pvfs, errno);
1048 stream_existed = name->stream_exists;
1050 /* if this was a stream create then create the stream as well */
1051 if (!name->stream_exists) {
1052 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1053 if (!NT_STATUS_IS_OK(status)) {
1059 /* re-resolve the open fd */
1060 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1061 if (!NT_STATUS_IS_OK(status)) {
1066 if (f->handle->name->stream_id == 0 &&
1067 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1068 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1069 /* for overwrite we need to replace file permissions */
1070 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1071 mode_t mode = pvfs_fileperms(pvfs, attrib);
1072 if (fchmod(fd, mode) == -1) {
1074 return pvfs_map_errno(pvfs, errno);
1076 name->dos.attrib = attrib;
1077 status = pvfs_dosattrib_save(pvfs, name, fd);
1078 if (!NT_STATUS_IS_OK(status)) {
1086 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1087 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
1089 io->generic.out.oplock_level = OPLOCK_NONE;
1091 io->generic.out.fnum = f->fnum;
1092 io->generic.out.create_action = stream_existed?
1093 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1094 io->generic.out.create_time = name->dos.create_time;
1095 io->generic.out.access_time = name->dos.access_time;
1096 io->generic.out.write_time = name->dos.write_time;
1097 io->generic.out.change_time = name->dos.change_time;
1098 io->generic.out.attrib = name->dos.attrib;
1099 io->generic.out.alloc_size = name->dos.alloc_size;
1100 io->generic.out.size = name->st.st_size;
1101 io->generic.out.file_type = FILE_TYPE_DISK;
1102 io->generic.out.ipc_state = 0;
1103 io->generic.out.is_directory = 0;
1105 /* success - keep the file handle */
1106 talloc_steal(f->pvfs, f);
1108 return NT_STATUS_OK;
1115 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1116 struct smbsrv_request *req, union smb_close *io)
1118 struct pvfs_state *pvfs = ntvfs->private_data;
1119 struct pvfs_file *f;
1120 struct utimbuf unix_times;
1122 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1123 return NT_STATUS_UNSUCCESSFUL;
1126 if (io->generic.level != RAW_CLOSE_CLOSE) {
1127 return ntvfs_map_close(req, io, ntvfs);
1130 f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
1132 return NT_STATUS_INVALID_HANDLE;
1135 if (!null_time(io->close.in.write_time)) {
1136 unix_times.actime = 0;
1137 unix_times.modtime = io->close.in.write_time;
1138 utime(f->handle->name->full_name, &unix_times);
1139 } else if (f->handle->sticky_write_time) {
1140 unix_times.actime = 0;
1141 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1142 utime(f->handle->name->full_name, &unix_times);
1147 return NT_STATUS_OK;
1152 logoff - close all file descriptors open by a vuid
1154 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1155 struct smbsrv_request *req)
1157 struct pvfs_state *pvfs = ntvfs->private_data;
1158 struct pvfs_file *f, *next;
1160 for (f=pvfs->open_files;f;f=next) {
1162 if (f->session == req->session) {
1167 return NT_STATUS_OK;
1172 exit - close files for the current pid
1174 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1175 struct smbsrv_request *req)
1177 struct pvfs_state *pvfs = ntvfs->private_data;
1178 struct pvfs_file *f, *next;
1180 for (f=pvfs->open_files;f;f=next) {
1182 if (f->smbpid == req->smbpid) {
1187 return NT_STATUS_OK;
1192 change the create options on an already open file
1194 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
1195 struct smbsrv_request *req,
1196 struct pvfs_file *f, uint32_t create_options)
1198 struct odb_lock *lck;
1201 if (f->handle->create_options == create_options) {
1202 return NT_STATUS_OK;
1205 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1206 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1207 return NT_STATUS_CANNOT_DELETE;
1210 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1212 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1215 status = odb_set_create_options(lck, f->handle, create_options);
1216 if (NT_STATUS_IS_OK(status)) {
1217 f->handle->create_options = create_options;
1225 determine if a file can be deleted, or if it is prevented by an
1228 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs, struct pvfs_filename *name)
1233 status = pvfs_locking_key(name, name, &key);
1234 if (!NT_STATUS_IS_OK(status)) {
1235 return NT_STATUS_NO_MEMORY;
1238 status = odb_can_open(pvfs->odb_context, &key,
1239 NTCREATEX_SHARE_ACCESS_READ |
1240 NTCREATEX_SHARE_ACCESS_WRITE |
1241 NTCREATEX_SHARE_ACCESS_DELETE,
1242 NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
1249 determine if a file can be renamed, or if it is prevented by an
1252 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs, struct pvfs_filename *name)
1257 status = pvfs_locking_key(name, name, &key);
1258 if (!NT_STATUS_IS_OK(status)) {
1259 return NT_STATUS_NO_MEMORY;
1262 status = odb_can_open(pvfs->odb_context, &key,
1263 NTCREATEX_SHARE_ACCESS_READ |
1264 NTCREATEX_SHARE_ACCESS_WRITE,