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 NTSTATUS status = pvfs_xattr_unlink_hook(h->pvfs, h->name->full_name);
74 if (!NT_STATUS_IS_OK(status)) {
75 DEBUG(0,("Warning: xattr rmdir hook failed for '%s' - %s\n",
76 h->name->full_name, nt_errstr(status)));
78 if (rmdir(h->name->full_name) != 0) {
79 DEBUG(0,("pvfs_close: failed to rmdir '%s' - %s\n",
80 h->name->full_name, strerror(errno)));
88 cleanup a open directory fnum
90 static int pvfs_dir_fnum_destructor(void *p)
92 struct pvfs_file *f = p;
93 DLIST_REMOVE(f->pvfs->open_files, f);
94 idr_remove(f->pvfs->idtree_fnum, f->fnum);
99 setup any EAs and the ACL on newly created files/directories
101 static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
102 struct smbsrv_request *req,
103 struct pvfs_filename *name,
109 /* setup any EAs that were asked for */
110 if (io->ntcreatex.in.ea_list) {
111 status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
112 io->ntcreatex.in.ea_list->num_eas,
113 io->ntcreatex.in.ea_list->eas);
114 if (!NT_STATUS_IS_OK(status)) {
119 /* setup an initial sec_desc if requested */
120 if (io->ntcreatex.in.sec_desc) {
121 union smb_setfileinfo set;
123 set.set_secdesc.file.fnum = fnum;
124 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
125 set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
127 status = pvfs_acl_set(pvfs, req, name, fd, SEC_STD_WRITE_DAC, &set);
129 /* otherwise setup an inherited acl from the parent */
130 status = pvfs_acl_inherit(pvfs, req, name, fd);
139 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
140 struct smbsrv_request *req,
141 struct pvfs_filename *name,
147 uint32_t create_action;
148 uint32_t access_mask = io->generic.in.access_mask;
150 if (name->stream_name) {
151 return NT_STATUS_NOT_A_DIRECTORY;
154 /* if the client says it must be a directory, and it isn't,
156 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
157 return NT_STATUS_NOT_A_DIRECTORY;
160 switch (io->generic.in.open_disposition) {
161 case NTCREATEX_DISP_OPEN_IF:
164 case NTCREATEX_DISP_OPEN:
166 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
170 case NTCREATEX_DISP_CREATE:
172 return NT_STATUS_OBJECT_NAME_COLLISION;
176 case NTCREATEX_DISP_OVERWRITE_IF:
177 case NTCREATEX_DISP_OVERWRITE:
178 case NTCREATEX_DISP_SUPERSEDE:
180 return NT_STATUS_INVALID_PARAMETER;
183 f = talloc(req, struct pvfs_file);
185 return NT_STATUS_NO_MEMORY;
188 f->handle = talloc(f, struct pvfs_file_handle);
189 if (f->handle == NULL) {
190 return NT_STATUS_NO_MEMORY;
193 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
195 return NT_STATUS_TOO_MANY_OPENED_FILES;
199 /* check the security descriptor */
200 status = pvfs_access_check(pvfs, req, name, &access_mask);
202 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
204 if (!NT_STATUS_IS_OK(status)) {
205 idr_remove(pvfs->idtree_fnum, fnum);
210 f->session = req->session;
211 f->smbpid = req->smbpid;
213 f->pending_list = NULL;
215 f->share_access = io->generic.in.share_access;
216 f->impersonation = io->generic.in.impersonation;
217 f->access_mask = access_mask;
219 f->handle->pvfs = pvfs;
220 f->handle->name = talloc_steal(f->handle, name);
222 f->handle->odb_locking_key = data_blob(NULL, 0);
223 f->handle->brl_locking_key = data_blob(NULL, 0);
224 f->handle->create_options = io->generic.in.create_options;
225 f->handle->seek_offset = 0;
226 f->handle->position = 0;
228 f->handle->sticky_write_time = False;
230 DLIST_ADD(pvfs->open_files, f);
232 /* TODO: should we check in the opendb? Do directory opens
233 follow the share_access rules? */
235 /* setup destructors to avoid leaks on abnormal termination */
236 talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
237 talloc_set_destructor(f, pvfs_dir_fnum_destructor);
240 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
241 mode_t mode = pvfs_fileperms(pvfs, attrib);
242 if (mkdir(name->full_name, mode) == -1) {
243 idr_remove(pvfs->idtree_fnum, fnum);
244 return pvfs_map_errno(pvfs,errno);
247 pvfs_xattr_unlink_hook(pvfs, name->full_name);
249 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
250 if (!NT_STATUS_IS_OK(status)) {
254 status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, fnum, io);
255 if (!NT_STATUS_IS_OK(status)) {
259 create_action = NTCREATEX_ACTION_CREATED;
261 create_action = NTCREATEX_ACTION_EXISTED;
265 idr_remove(pvfs->idtree_fnum, fnum);
266 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
269 /* the open succeeded, keep this handle permanently */
270 talloc_steal(pvfs, f);
272 io->generic.out.oplock_level = OPLOCK_NONE;
273 io->generic.out.fnum = f->fnum;
274 io->generic.out.create_action = create_action;
275 io->generic.out.create_time = name->dos.create_time;
276 io->generic.out.access_time = name->dos.access_time;
277 io->generic.out.write_time = name->dos.write_time;
278 io->generic.out.change_time = name->dos.change_time;
279 io->generic.out.attrib = name->dos.attrib;
280 io->generic.out.alloc_size = name->dos.alloc_size;
281 io->generic.out.size = name->st.st_size;
282 io->generic.out.file_type = FILE_TYPE_DISK;
283 io->generic.out.ipc_state = 0;
284 io->generic.out.is_directory = 1;
289 idr_remove(pvfs->idtree_fnum, fnum);
290 rmdir(name->full_name);
295 destroy a struct pvfs_file_handle
297 static int pvfs_handle_destructor(void *p)
299 struct pvfs_file_handle *h = p;
301 /* the write time is no longer sticky */
302 if (h->sticky_write_time) {
304 status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
305 if (NT_STATUS_IS_OK(status)) {
306 h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
307 pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
311 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
312 h->name->stream_name) {
314 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
315 if (!NT_STATUS_IS_OK(status)) {
316 DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
317 h->name->stream_name, h->name->full_name));
322 if (close(h->fd) != 0) {
323 DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
324 h->fd, h->name->full_name, strerror(errno)));
329 if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
330 h->name->stream_name == NULL) {
332 status = pvfs_xattr_unlink_hook(h->pvfs, h->name->full_name);
333 if (!NT_STATUS_IS_OK(status)) {
334 DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
335 h->name->full_name, nt_errstr(status)));
337 if (unlink(h->name->full_name) != 0) {
338 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
339 h->name->full_name, strerror(errno)));
343 if (h->have_opendb_entry) {
344 struct odb_lock *lck;
347 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
349 DEBUG(0,("Unable to lock opendb for close\n"));
353 status = odb_close_file(lck, h);
354 if (!NT_STATUS_IS_OK(status)) {
355 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
356 h->name->full_name, nt_errstr(status)));
367 destroy a struct pvfs_file
369 static int pvfs_fnum_destructor(void *p)
371 struct pvfs_file *f = p;
373 DLIST_REMOVE(f->pvfs->open_files, f);
374 pvfs_lock_close(f->pvfs, f);
375 idr_remove(f->pvfs->idtree_fnum, f->fnum);
382 form the lock context used for opendb locking. Note that we must
383 zero here to take account of possible padding on some architectures
385 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
386 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
392 ZERO_STRUCT(lock_context);
394 lock_context.device = name->st.st_dev;
395 lock_context.inode = name->st.st_ino;
397 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
398 if (key->data == NULL) {
399 return NT_STATUS_NO_MEMORY;
406 form the lock context used for byte range locking. This is separate
407 from the locking key used for opendb locking as it needs to take
408 account of file streams (each stream is a separate byte range
411 static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name,
412 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
416 status = pvfs_locking_key(name, mem_ctx, &odb_key);
417 if (!NT_STATUS_IS_OK(status)) {
420 if (name->stream_name == NULL) {
424 *key = data_blob_talloc(mem_ctx, NULL,
425 odb_key.length + strlen(name->stream_name) + 1);
426 if (key->data == NULL) {
427 return NT_STATUS_NO_MEMORY;
429 memcpy(key->data, odb_key.data, odb_key.length);
430 memcpy(key->data + odb_key.length,
431 name->stream_name, strlen(name->stream_name)+1);
432 data_blob_free(&odb_key);
440 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
441 struct smbsrv_request *req,
442 struct pvfs_filename *name,
448 struct odb_lock *lck;
449 uint32_t create_options = io->generic.in.create_options;
450 uint32_t share_access = io->generic.in.share_access;
451 uint32_t access_mask = io->generic.in.access_mask;
455 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
456 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
457 return NT_STATUS_CANNOT_DELETE;
460 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
461 if (!NT_STATUS_IS_OK(status)) {
465 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
471 f = talloc(req, struct pvfs_file);
473 return NT_STATUS_NO_MEMORY;
476 f->handle = talloc(f, struct pvfs_file_handle);
477 if (f->handle == NULL) {
478 return NT_STATUS_NO_MEMORY;
481 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
483 return NT_STATUS_TOO_MANY_OPENED_FILES;
486 attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
487 mode = pvfs_fileperms(pvfs, attrib);
489 /* create the file */
490 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
492 idr_remove(pvfs->idtree_fnum, fnum);
493 return pvfs_map_errno(pvfs, errno);
496 pvfs_xattr_unlink_hook(pvfs, name->full_name);
498 /* if this was a stream create then create the stream as well */
499 if (name->stream_name) {
500 status = pvfs_stream_create(pvfs, name, fd);
501 if (!NT_STATUS_IS_OK(status)) {
502 idr_remove(pvfs->idtree_fnum, fnum);
508 /* re-resolve the open fd */
509 status = pvfs_resolve_name_fd(pvfs, fd, name);
510 if (!NT_STATUS_IS_OK(status)) {
511 idr_remove(pvfs->idtree_fnum, fnum);
516 name->dos.attrib = attrib;
517 status = pvfs_dosattrib_save(pvfs, name, fd);
518 if (!NT_STATUS_IS_OK(status)) {
523 status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, fnum, io);
524 if (!NT_STATUS_IS_OK(status)) {
528 /* form the lock context used for byte range locking and
530 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
531 if (!NT_STATUS_IS_OK(status)) {
535 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
536 if (!NT_STATUS_IS_OK(status)) {
540 /* grab a lock on the open file record */
541 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
543 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
545 /* we were supposed to do a blocking lock, so something
547 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
551 status = odb_open_file(lck, f->handle, name->stream_id,
552 share_access, create_options, access_mask);
554 if (!NT_STATUS_IS_OK(status)) {
555 /* bad news, we must have hit a race - we don't delete the file
556 here as the most likely scenario is that someone else created
557 the file at the same time */
558 idr_remove(pvfs->idtree_fnum, fnum);
564 f->session = req->session;
565 f->smbpid = req->smbpid;
567 f->pending_list = NULL;
569 f->share_access = io->generic.in.share_access;
570 f->access_mask = access_mask;
571 f->impersonation = io->generic.in.impersonation;
573 f->handle->pvfs = pvfs;
574 f->handle->name = talloc_steal(f->handle, name);
576 f->handle->create_options = io->generic.in.create_options;
577 f->handle->seek_offset = 0;
578 f->handle->position = 0;
580 f->handle->have_opendb_entry = True;
581 f->handle->sticky_write_time = False;
583 DLIST_ADD(pvfs->open_files, f);
585 /* setup a destructor to avoid file descriptor leaks on
586 abnormal termination */
587 talloc_set_destructor(f, pvfs_fnum_destructor);
588 talloc_set_destructor(f->handle, pvfs_handle_destructor);
591 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
592 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
594 io->generic.out.oplock_level = OPLOCK_NONE;
596 io->generic.out.fnum = f->fnum;
597 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
598 io->generic.out.create_time = name->dos.create_time;
599 io->generic.out.access_time = name->dos.access_time;
600 io->generic.out.write_time = name->dos.write_time;
601 io->generic.out.change_time = name->dos.change_time;
602 io->generic.out.attrib = name->dos.attrib;
603 io->generic.out.alloc_size = name->dos.alloc_size;
604 io->generic.out.size = name->st.st_size;
605 io->generic.out.file_type = FILE_TYPE_DISK;
606 io->generic.out.ipc_state = 0;
607 io->generic.out.is_directory = 0;
609 /* success - keep the file handle */
610 talloc_steal(pvfs, f);
615 idr_remove(pvfs->idtree_fnum, fnum);
617 unlink(name->full_name);
623 state of a pending open retry
625 struct pvfs_open_retry {
626 struct ntvfs_module_context *ntvfs;
627 struct smbsrv_request *req;
630 DATA_BLOB odb_locking_key;
633 /* destroy a pending open request */
634 static int pvfs_retry_destructor(void *ptr)
636 struct pvfs_open_retry *r = ptr;
637 struct pvfs_state *pvfs = r->ntvfs->private_data;
638 if (r->odb_locking_key.data) {
639 struct odb_lock *lck;
640 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
642 odb_remove_pending(lck, r);
652 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
654 struct pvfs_open_retry *r = private;
655 struct ntvfs_module_context *ntvfs = r->ntvfs;
656 struct smbsrv_request *req = r->req;
657 union smb_open *io = r->io;
660 /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
661 just a bug in their server, but we better do the same */
662 if (reason == PVFS_WAIT_CANCEL) {
666 talloc_free(r->wait_handle);
668 if (reason == PVFS_WAIT_TIMEOUT) {
669 /* if it timed out, then give the failure
672 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
673 req->async_states->send_fn(req);
677 /* the pending odb entry is already removed. We use a null locking
678 key to indicate this */
679 data_blob_free(&r->odb_locking_key);
682 /* try the open again, which could trigger another retry setup
683 if it wants to, so we have to unmark the async flag so we
684 will know if it does a second async reply */
685 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
687 status = pvfs_open(ntvfs, req, io);
688 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
689 /* the 2nd try also replied async, so we don't send
694 /* re-mark it async, just in case someone up the chain does
696 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
698 /* send the reply up the chain */
699 req->async_states->status = status;
700 req->async_states->send_fn(req);
705 special handling for openx DENY_DOS semantics
707 This function attempts a reference open using an existing handle. If its allowed,
708 then it returns NT_STATUS_OK, otherwise it returns any other code and normal
709 open processing continues.
711 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
712 struct smbsrv_request *req, union smb_open *io,
713 struct pvfs_file *f, struct odb_lock *lck)
715 struct pvfs_state *pvfs = ntvfs->private_data;
716 struct pvfs_file *f2;
717 struct pvfs_filename *name;
719 /* search for an existing open with the right parameters. Note
720 the magic ntcreatex options flag, which is set in the
721 generic mapping code. This might look ugly, but its
722 actually pretty much now w2k does it internally as well.
724 If you look at the BASE-DENYDOS test you will see that a
725 DENY_DOS is a very special case, and in the right
726 circumstances you actually get the _same_ handle back
727 twice, rather than a new handle.
729 for (f2=pvfs->open_files;f2;f2=f2->next) {
731 f2->session == req->session &&
732 f2->smbpid == req->smbpid &&
733 (f2->handle->create_options &
734 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
735 NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
736 (f2->access_mask & SEC_FILE_WRITE_DATA) &&
737 StrCaseCmp(f2->handle->name->original_name,
738 io->generic.in.fname)==0) {
744 return NT_STATUS_SHARING_VIOLATION;
747 /* quite an insane set of semantics ... */
748 if (is_exe_filename(io->generic.in.fname) &&
749 (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
750 return NT_STATUS_SHARING_VIOLATION;
754 setup a reference to the existing handle
756 talloc_free(f->handle);
757 f->handle = talloc_reference(f, f2->handle);
761 name = f->handle->name;
763 io->generic.out.oplock_level = OPLOCK_NONE;
764 io->generic.out.fnum = f->fnum;
765 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
766 io->generic.out.create_time = name->dos.create_time;
767 io->generic.out.access_time = name->dos.access_time;
768 io->generic.out.write_time = name->dos.write_time;
769 io->generic.out.change_time = name->dos.change_time;
770 io->generic.out.attrib = name->dos.attrib;
771 io->generic.out.alloc_size = name->dos.alloc_size;
772 io->generic.out.size = name->st.st_size;
773 io->generic.out.file_type = FILE_TYPE_DISK;
774 io->generic.out.ipc_state = 0;
775 io->generic.out.is_directory = 0;
777 talloc_steal(f->pvfs, f);
785 setup for a open retry after a sharing violation
787 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
788 struct smbsrv_request *req,
791 struct odb_lock *lck)
793 struct pvfs_state *pvfs = ntvfs->private_data;
794 struct pvfs_open_retry *r;
796 struct timeval end_time;
798 if (io->generic.in.create_options &
799 (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
800 /* see if we can satisfy the request using the special DENY_DOS
802 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
803 if (NT_STATUS_IS_OK(status)) {
808 r = talloc(req, struct pvfs_open_retry);
810 return NT_STATUS_NO_MEMORY;
816 r->odb_locking_key = data_blob_talloc(r,
817 f->handle->odb_locking_key.data,
818 f->handle->odb_locking_key.length);
820 end_time = timeval_add(&req->request_time, 0, pvfs->sharing_violation_delay);
822 /* setup a pending lock */
823 status = odb_open_file_pending(lck, r);
824 if (!NT_STATUS_IS_OK(status)) {
831 talloc_set_destructor(r, pvfs_retry_destructor);
833 r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
835 if (r->wait_handle == NULL) {
836 return NT_STATUS_NO_MEMORY;
839 talloc_steal(pvfs, r);
847 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
848 struct smbsrv_request *req, union smb_open *io)
850 struct pvfs_state *pvfs = ntvfs->private_data;
852 struct pvfs_filename *name;
856 struct odb_lock *lck;
857 uint32_t create_options;
858 uint32_t share_access;
859 uint32_t access_mask;
862 /* use the generic mapping code to avoid implementing all the
863 different open calls. */
864 if (io->generic.level != RAW_OPEN_GENERIC &&
865 io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
866 return ntvfs_map_open(req, io, ntvfs);
869 /* resolve the cifs name to a posix name */
870 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
871 PVFS_RESOLVE_STREAMS, &name);
872 if (!NT_STATUS_IS_OK(status)) {
876 /* directory opens are handled separately */
877 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
878 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
879 return pvfs_open_directory(pvfs, req, name, io);
882 /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
883 open doesn't match */
884 io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
886 create_options = io->generic.in.create_options;
887 share_access = io->generic.in.share_access;
888 access_mask = io->generic.in.access_mask;
890 /* certain create options are not allowed */
891 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
892 !(access_mask & SEC_STD_DELETE)) {
893 return NT_STATUS_INVALID_PARAMETER;
896 switch (io->generic.in.open_disposition) {
897 case NTCREATEX_DISP_SUPERSEDE:
901 case NTCREATEX_DISP_OVERWRITE_IF:
905 case NTCREATEX_DISP_OPEN:
906 if (!name->stream_exists) {
907 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
912 case NTCREATEX_DISP_OVERWRITE:
913 if (!name->stream_exists) {
914 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
919 case NTCREATEX_DISP_CREATE:
920 if (name->stream_exists) {
921 return NT_STATUS_OBJECT_NAME_COLLISION;
926 case NTCREATEX_DISP_OPEN_IF:
931 return NT_STATUS_INVALID_PARAMETER;
934 /* handle creating a new file separately */
936 status = pvfs_create_file(pvfs, req, name, io);
937 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
941 /* we've hit a race - the file was created during this call */
942 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
946 /* try re-resolving the name */
947 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
948 if (!NT_STATUS_IS_OK(status)) {
951 /* fall through to a normal open */
954 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
955 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
956 return NT_STATUS_CANNOT_DELETE;
959 /* check the security descriptor */
960 status = pvfs_access_check(pvfs, req, name, &access_mask);
961 if (!NT_STATUS_IS_OK(status)) {
965 f = talloc(req, struct pvfs_file);
967 return NT_STATUS_NO_MEMORY;
970 f->handle = talloc(f, struct pvfs_file_handle);
971 if (f->handle == NULL) {
972 return NT_STATUS_NO_MEMORY;
975 /* allocate a fnum */
976 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
978 return NT_STATUS_TOO_MANY_OPENED_FILES;
982 f->session = req->session;
983 f->smbpid = req->smbpid;
985 f->pending_list = NULL;
987 f->share_access = io->generic.in.share_access;
988 f->access_mask = access_mask;
989 f->impersonation = io->generic.in.impersonation;
991 f->handle->pvfs = pvfs;
993 f->handle->name = talloc_steal(f->handle, name);
994 f->handle->create_options = io->generic.in.create_options;
995 f->handle->seek_offset = 0;
996 f->handle->position = 0;
998 f->handle->have_opendb_entry = False;
999 f->handle->sticky_write_time = False;
1001 /* form the lock context used for byte range locking and
1003 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
1004 if (!NT_STATUS_IS_OK(status)) {
1005 idr_remove(pvfs->idtree_fnum, f->fnum);
1009 status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
1010 if (!NT_STATUS_IS_OK(status)) {
1011 idr_remove(pvfs->idtree_fnum, f->fnum);
1015 /* get a lock on this file before the actual open */
1016 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1018 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1020 /* we were supposed to do a blocking lock, so something
1022 idr_remove(pvfs->idtree_fnum, fnum);
1023 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1026 DLIST_ADD(pvfs->open_files, f);
1028 /* setup a destructor to avoid file descriptor leaks on
1029 abnormal termination */
1030 talloc_set_destructor(f, pvfs_fnum_destructor);
1031 talloc_set_destructor(f->handle, pvfs_handle_destructor);
1034 /* see if we are allowed to open at the same time as existing opens */
1035 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
1036 share_access, create_options, access_mask);
1038 /* on a sharing violation we need to retry when the file is closed by
1039 the other user, or after 1 second */
1040 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1041 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1042 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1045 if (!NT_STATUS_IS_OK(status)) {
1050 f->handle->have_opendb_entry = True;
1052 if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1058 /* do the actual open */
1059 fd = open(f->handle->name->full_name, flags);
1062 return pvfs_map_errno(f->pvfs, errno);
1067 stream_existed = name->stream_exists;
1069 /* if this was a stream create then create the stream as well */
1070 if (!name->stream_exists) {
1071 if (!(access_mask & SEC_FILE_WRITE_ATTRIBUTE)) {
1072 return NT_STATUS_ACCESS_DENIED;
1074 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1075 if (!NT_STATUS_IS_OK(status)) {
1081 /* re-resolve the open fd */
1082 status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1083 if (!NT_STATUS_IS_OK(status)) {
1088 if (f->handle->name->stream_id == 0 &&
1089 (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1090 io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1091 /* for overwrite we need to replace file permissions */
1092 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1093 mode_t mode = pvfs_fileperms(pvfs, attrib);
1094 if (fchmod(fd, mode) == -1) {
1096 return pvfs_map_errno(pvfs, errno);
1098 name->dos.attrib = attrib;
1099 status = pvfs_dosattrib_save(pvfs, name, fd);
1100 if (!NT_STATUS_IS_OK(status)) {
1108 if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1109 io->generic.out.oplock_level = OPLOCK_EXCLUSIVE;
1111 io->generic.out.oplock_level = OPLOCK_NONE;
1113 io->generic.out.fnum = f->fnum;
1114 io->generic.out.create_action = stream_existed?
1115 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1116 io->generic.out.create_time = name->dos.create_time;
1117 io->generic.out.access_time = name->dos.access_time;
1118 io->generic.out.write_time = name->dos.write_time;
1119 io->generic.out.change_time = name->dos.change_time;
1120 io->generic.out.attrib = name->dos.attrib;
1121 io->generic.out.alloc_size = name->dos.alloc_size;
1122 io->generic.out.size = name->st.st_size;
1123 io->generic.out.file_type = FILE_TYPE_DISK;
1124 io->generic.out.ipc_state = 0;
1125 io->generic.out.is_directory = 0;
1127 /* success - keep the file handle */
1128 talloc_steal(f->pvfs, f);
1130 return NT_STATUS_OK;
1137 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1138 struct smbsrv_request *req, union smb_close *io)
1140 struct pvfs_state *pvfs = ntvfs->private_data;
1141 struct pvfs_file *f;
1142 struct utimbuf unix_times;
1144 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1145 return NT_STATUS_UNSUCCESSFUL;
1148 if (io->generic.level != RAW_CLOSE_CLOSE) {
1149 return ntvfs_map_close(req, io, ntvfs);
1152 f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
1154 return NT_STATUS_INVALID_HANDLE;
1157 if (!null_time(io->close.in.write_time)) {
1158 unix_times.actime = 0;
1159 unix_times.modtime = io->close.in.write_time;
1160 utime(f->handle->name->full_name, &unix_times);
1161 } else if (f->handle->sticky_write_time) {
1162 unix_times.actime = 0;
1163 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1164 utime(f->handle->name->full_name, &unix_times);
1169 return NT_STATUS_OK;
1174 logoff - close all file descriptors open by a vuid
1176 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1177 struct smbsrv_request *req)
1179 struct pvfs_state *pvfs = ntvfs->private_data;
1180 struct pvfs_file *f, *next;
1182 for (f=pvfs->open_files;f;f=next) {
1184 if (f->session == req->session) {
1189 return NT_STATUS_OK;
1194 exit - close files for the current pid
1196 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1197 struct smbsrv_request *req)
1199 struct pvfs_state *pvfs = ntvfs->private_data;
1200 struct pvfs_file *f, *next;
1202 for (f=pvfs->open_files;f;f=next) {
1204 if (f->smbpid == req->smbpid) {
1209 return NT_STATUS_OK;
1214 change the create options on an already open file
1216 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
1217 struct smbsrv_request *req,
1218 struct pvfs_file *f, uint32_t create_options)
1220 struct odb_lock *lck;
1223 if (f->handle->create_options == create_options) {
1224 return NT_STATUS_OK;
1227 if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1228 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1229 return NT_STATUS_CANNOT_DELETE;
1232 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1234 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1237 status = odb_set_create_options(lck, f->handle, create_options);
1238 if (NT_STATUS_IS_OK(status)) {
1239 f->handle->create_options = create_options;
1247 determine if a file can be deleted, or if it is prevented by an
1250 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
1251 struct smbsrv_request *req,
1252 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 |
1265 NTCREATEX_SHARE_ACCESS_DELETE,
1266 NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
1269 if (NT_STATUS_IS_OK(status)) {
1270 status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
1277 determine if a file can be renamed, or if it is prevented by an
1280 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs, struct pvfs_filename *name)
1285 status = pvfs_locking_key(name, name, &key);
1286 if (!NT_STATUS_IS_OK(status)) {
1287 return NT_STATUS_NO_MEMORY;
1290 status = odb_can_open(pvfs->odb_context, &key,
1291 NTCREATEX_SHARE_ACCESS_READ |
1292 NTCREATEX_SHARE_ACCESS_WRITE,