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.
23 #include "include/includes.h"
24 #include "vfs_posix.h"
25 #include "system/time.h"
26 #include "system/filesys.h"
27 #include "dlinklist.h"
30 create file handles with convenient numbers for sniffers
32 #define PVFS_MIN_FILE_FNUM 0x100
33 #define PVFS_MIN_NEW_FNUM 0x200
34 #define PVFS_MIN_DIR_FNUM 0x300
37 find open file handle given fnum
39 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
40 struct smbsrv_request *req, uint16_t fnum)
44 f = idr_find(pvfs->idtree_fnum, fnum);
49 if (req->session != f->session) {
50 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n",
60 cleanup a open directory handle
62 static int pvfs_dir_fd_destructor(void *p)
64 struct pvfs_file *f = p;
65 DLIST_REMOVE(f->pvfs->open_files, f);
66 idr_remove(f->pvfs->idtree_fnum, f->fnum);
68 if (f->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
69 if (rmdir(f->name->full_name) != 0) {
70 DEBUG(0,("pvfs_close: failed to rmdir '%s' - %s\n",
71 f->name->full_name, strerror(errno)));
82 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
83 struct smbsrv_request *req,
84 struct pvfs_filename *name,
90 uint32_t create_action;
92 /* if the client says it must be a directory, and it isn't,
94 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
95 return NT_STATUS_NOT_A_DIRECTORY;
98 switch (io->generic.in.open_disposition) {
99 case NTCREATEX_DISP_OPEN_IF:
102 case NTCREATEX_DISP_OPEN:
104 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
108 case NTCREATEX_DISP_CREATE:
110 return NT_STATUS_OBJECT_NAME_COLLISION;
114 case NTCREATEX_DISP_OVERWRITE_IF:
115 case NTCREATEX_DISP_OVERWRITE:
116 case NTCREATEX_DISP_SUPERSEDE:
118 return NT_STATUS_INVALID_PARAMETER;
121 f = talloc_p(req, struct pvfs_file);
123 return NT_STATUS_NO_MEMORY;
126 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
129 return NT_STATUS_TOO_MANY_OPENED_FILES;
134 f->name = talloc_steal(f, name);
135 f->session = req->session;
136 f->smbpid = req->smbpid;
138 f->pending_list = NULL;
140 f->locking_key = data_blob(NULL, 0);
141 f->create_options = io->generic.in.create_options;
142 f->share_access = io->generic.in.share_access;
146 DLIST_ADD(pvfs->open_files, f);
148 /* TODO: should we check in the opendb? Do directory opens
149 follow the share_access rules? */
152 /* setup a destructor to avoid leaks on abnormal termination */
153 talloc_set_destructor(f, pvfs_dir_fd_destructor);
156 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
157 mode_t mode = pvfs_fileperms(pvfs, attrib);
158 if (mkdir(name->full_name, mode) == -1) {
159 return pvfs_map_errno(pvfs,errno);
161 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
162 PVFS_RESOLVE_NO_WILDCARD, &name);
163 if (!NT_STATUS_IS_OK(status)) {
166 create_action = NTCREATEX_ACTION_CREATED;
168 create_action = NTCREATEX_ACTION_EXISTED;
172 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
175 /* the open succeeded, keep this handle permanently */
176 talloc_steal(pvfs, f);
178 io->generic.out.oplock_level = NO_OPLOCK;
179 io->generic.out.fnum = f->fnum;
180 io->generic.out.create_action = create_action;
181 io->generic.out.create_time = name->dos.create_time;
182 io->generic.out.access_time = name->dos.access_time;
183 io->generic.out.write_time = name->dos.write_time;
184 io->generic.out.change_time = name->dos.change_time;
185 io->generic.out.attrib = name->dos.attrib;
186 io->generic.out.alloc_size = name->dos.alloc_size;
187 io->generic.out.size = name->st.st_size;
188 io->generic.out.file_type = FILE_TYPE_DISK;
189 io->generic.out.ipc_state = 0;
190 io->generic.out.is_directory = 1;
197 by using a destructor we make sure that abnormal cleanup will not
198 leak file descriptors (assuming at least the top level pointer is freed, which
199 will cascade down to here)
201 static int pvfs_fd_destructor(void *p)
203 struct pvfs_file *f = p;
204 struct odb_lock *lck;
207 DLIST_REMOVE(f->pvfs->open_files, f);
209 pvfs_lock_close(f->pvfs, f);
216 idr_remove(f->pvfs->idtree_fnum, f->fnum);
218 if (f->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
219 if (unlink(f->name->full_name) != 0) {
220 DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
221 f->name->full_name, strerror(errno)));
225 lck = odb_lock(f, f->pvfs->odb_context, &f->locking_key);
227 DEBUG(0,("Unable to lock opendb for close\n"));
231 status = odb_close_file(lck, f->fnum);
232 if (!NT_STATUS_IS_OK(status)) {
233 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
234 f->name->full_name, nt_errstr(status)));
244 form the lock context used for byte range locking and opendb
245 locking. Note that we must zero here to take account of
246 possible padding on some architectures
248 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
249 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
255 ZERO_STRUCT(lock_context);
257 lock_context.device = name->st.st_dev;
258 lock_context.inode = name->st.st_ino;
260 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
261 if (key->data == NULL) {
262 return NT_STATUS_NO_MEMORY;
272 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
273 struct smbsrv_request *req,
274 struct pvfs_filename *name,
280 struct odb_lock *lck;
281 uint32_t create_options = io->generic.in.create_options;
282 uint32_t share_access = io->generic.in.share_access;
283 uint32_t access_mask = io->generic.in.access_mask;
286 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
287 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
288 return NT_STATUS_CANNOT_DELETE;
291 if (access_mask & SEC_RIGHT_MAXIMUM_ALLOWED) {
292 access_mask = GENERIC_RIGHTS_FILE_READ | GENERIC_RIGHTS_FILE_WRITE;
295 if ((access_mask & SA_RIGHT_FILE_READ_EXEC) &&
296 (access_mask & SA_RIGHT_FILE_WRITE_APPEND)) {
298 } else if (access_mask & SA_RIGHT_FILE_WRITE_APPEND) {
304 f = talloc_p(req, struct pvfs_file);
306 return NT_STATUS_NO_MEMORY;
309 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
311 return NT_STATUS_TOO_MANY_OPENED_FILES;
314 mode = pvfs_fileperms(pvfs, io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE);
316 /* create the file */
317 fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
319 idr_remove(pvfs->idtree_fnum, fnum);
320 return pvfs_map_errno(pvfs, errno);
323 /* re-resolve the open fd */
324 status = pvfs_resolve_name_fd(pvfs, fd, name);
325 if (!NT_STATUS_IS_OK(status)) {
326 idr_remove(pvfs->idtree_fnum, fnum);
331 /* form the lock context used for byte range locking and
333 status = pvfs_locking_key(name, f, &f->locking_key);
334 if (!NT_STATUS_IS_OK(status)) {
335 idr_remove(pvfs->idtree_fnum, fnum);
340 /* grab a lock on the open file record */
341 lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
343 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
345 /* we were supposed to do a blocking lock, so something
347 idr_remove(pvfs->idtree_fnum, fnum);
349 return NT_STATUS_INTERNAL_DB_CORRUPTION;
352 status = odb_open_file(lck, fnum, share_access, create_options, access_mask);
353 if (!NT_STATUS_IS_OK(status)) {
354 /* bad news, we must have hit a race */
355 idr_remove(pvfs->idtree_fnum, fnum);
362 f->name = talloc_steal(f, name);
363 f->session = req->session;
364 f->smbpid = req->smbpid;
366 f->pending_list = NULL;
368 f->create_options = io->generic.in.create_options;
369 f->share_access = io->generic.in.share_access;
370 f->access_mask = access_mask;
374 DLIST_ADD(pvfs->open_files, f);
376 /* setup a destructor to avoid file descriptor leaks on
377 abnormal termination */
378 talloc_set_destructor(f, pvfs_fd_destructor);
380 io->generic.out.oplock_level = NO_OPLOCK;
381 io->generic.out.fnum = f->fnum;
382 io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
383 io->generic.out.create_time = name->dos.create_time;
384 io->generic.out.access_time = name->dos.access_time;
385 io->generic.out.write_time = name->dos.write_time;
386 io->generic.out.change_time = name->dos.change_time;
387 io->generic.out.attrib = name->dos.attrib;
388 io->generic.out.alloc_size = name->dos.alloc_size;
389 io->generic.out.size = name->st.st_size;
390 io->generic.out.file_type = FILE_TYPE_DISK;
391 io->generic.out.ipc_state = 0;
392 io->generic.out.is_directory = 0;
394 /* success - keep the file handle */
395 talloc_steal(pvfs, f);
404 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
405 struct smbsrv_request *req, union smb_open *io)
407 struct pvfs_state *pvfs = ntvfs->private_data;
409 struct pvfs_filename *name;
413 struct odb_lock *lck;
414 uint32_t create_options;
415 uint32_t share_access;
416 uint32_t access_mask;
418 /* use the generic mapping code to avoid implementing all the
419 different open calls. This won't allow openx to work
420 perfectly as the mapping code has no way of knowing if two
421 opens are on the same connection, so this will need to
423 if (io->generic.level != RAW_OPEN_GENERIC) {
424 return ntvfs_map_open(req, io, ntvfs);
427 /* resolve the cifs name to a posix name */
428 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
429 PVFS_RESOLVE_NO_WILDCARD, &name);
430 if (!NT_STATUS_IS_OK(status)) {
434 /* directory opens are handled separately */
435 if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
436 (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
437 return pvfs_open_directory(pvfs, req, name, io);
440 create_options = io->generic.in.create_options;
441 share_access = io->generic.in.share_access;
442 access_mask = io->generic.in.access_mask;
444 if (access_mask & SEC_RIGHT_MAXIMUM_ALLOWED) {
445 if (name->exists && (name->dos.attrib & FILE_ATTRIBUTE_READONLY)) {
446 access_mask = GENERIC_RIGHTS_FILE_READ;
448 access_mask = GENERIC_RIGHTS_FILE_READ | GENERIC_RIGHTS_FILE_WRITE;
452 /* certain create options are not allowed */
453 if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
454 !(access_mask & STD_RIGHT_DELETE_ACCESS)) {
455 return NT_STATUS_INVALID_PARAMETER;
458 switch (io->generic.in.open_disposition) {
459 case NTCREATEX_DISP_SUPERSEDE:
463 case NTCREATEX_DISP_OVERWRITE_IF:
467 case NTCREATEX_DISP_OPEN:
469 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
474 case NTCREATEX_DISP_OVERWRITE:
476 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
481 case NTCREATEX_DISP_CREATE:
483 return NT_STATUS_OBJECT_NAME_COLLISION;
488 case NTCREATEX_DISP_OPEN_IF:
493 return NT_STATUS_INVALID_PARAMETER;
496 if ((access_mask & SA_RIGHT_FILE_READ_EXEC) &&
497 (access_mask & SA_RIGHT_FILE_WRITE_APPEND)) {
499 } else if (access_mask & SA_RIGHT_FILE_WRITE_APPEND) {
505 /* handle creating a new file separately */
507 status = pvfs_create_file(pvfs, req, name, io);
508 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
512 /* we've hit a race - the file was created during this call */
513 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
517 /* try re-resolving the name */
518 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
519 PVFS_RESOLVE_NO_WILDCARD, &name);
520 if (!NT_STATUS_IS_OK(status)) {
523 /* fall through to a normal open */
526 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
527 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
528 return NT_STATUS_CANNOT_DELETE;
531 f = talloc_p(req, struct pvfs_file);
533 return NT_STATUS_NO_MEMORY;
536 /* allocate a fnum */
537 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
539 return NT_STATUS_TOO_MANY_OPENED_FILES;
542 /* form the lock context used for byte range locking and
544 status = pvfs_locking_key(name, f, &f->locking_key);
545 if (!NT_STATUS_IS_OK(status)) {
546 idr_remove(pvfs->idtree_fnum, fnum);
550 /* get a lock on this file before the actual open */
551 lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
553 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
555 /* we were supposed to do a blocking lock, so something
557 idr_remove(pvfs->idtree_fnum, fnum);
558 return NT_STATUS_INTERNAL_DB_CORRUPTION;
561 /* see if we are allowed to open at the same time as existing opens */
562 status = odb_open_file(lck, fnum, share_access, create_options, access_mask);
563 if (!NT_STATUS_IS_OK(status)) {
564 idr_remove(pvfs->idtree_fnum, fnum);
570 f->name = talloc_steal(f, name);
571 f->session = req->session;
572 f->smbpid = req->smbpid;
574 f->pending_list = NULL;
576 f->create_options = io->generic.in.create_options;
577 f->share_access = io->generic.in.share_access;
578 f->access_mask = access_mask;
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_fd_destructor);
588 /* do the actual open */
589 fd = open(name->full_name, flags);
591 return pvfs_map_errno(pvfs, errno);
596 /* re-resolve the open fd */
597 status = pvfs_resolve_name_fd(pvfs, fd, name);
598 if (!NT_STATUS_IS_OK(status)) {
602 io->generic.out.oplock_level = NO_OPLOCK;
603 io->generic.out.fnum = f->fnum;
604 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
605 io->generic.out.create_time = name->dos.create_time;
606 io->generic.out.access_time = name->dos.access_time;
607 io->generic.out.write_time = name->dos.write_time;
608 io->generic.out.change_time = name->dos.change_time;
609 io->generic.out.attrib = name->dos.attrib;
610 io->generic.out.alloc_size = name->dos.alloc_size;
611 io->generic.out.size = name->st.st_size;
612 io->generic.out.file_type = FILE_TYPE_DISK;
613 io->generic.out.ipc_state = 0;
614 io->generic.out.is_directory = 0;
616 /* success - keep the file handle */
617 talloc_steal(pvfs, f);
626 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
627 struct smbsrv_request *req, union smb_close *io)
629 struct pvfs_state *pvfs = ntvfs->private_data;
632 struct utimbuf unix_times;
634 if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
635 return NT_STATUS_UNSUCCESSFUL;
638 if (io->generic.level != RAW_CLOSE_CLOSE) {
639 return ntvfs_map_close(req, io, ntvfs);
642 f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
644 return NT_STATUS_INVALID_HANDLE;
647 if (!null_time(io->close.in.write_time)) {
648 unix_times.actime = 0;
649 unix_times.modtime = io->close.in.write_time;
650 utime(f->name->full_name, &unix_times);
654 close(f->fd) == -1) {
655 status = pvfs_map_errno(pvfs, errno);
657 status = NT_STATUS_OK;
661 /* the destructor takes care of the rest */
669 logoff - close all file descriptors open by a vuid
671 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
672 struct smbsrv_request *req)
674 struct pvfs_state *pvfs = ntvfs->private_data;
675 struct pvfs_file *f, *next;
677 for (f=pvfs->open_files;f;f=next) {
679 if (f->session == req->session) {
680 DLIST_REMOVE(pvfs->open_files, f);
690 exit - close files for the current pid
692 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
693 struct smbsrv_request *req)
695 struct pvfs_state *pvfs = ntvfs->private_data;
696 struct pvfs_file *f, *next;
698 for (f=pvfs->open_files;f;f=next) {
700 if (f->smbpid == req->smbpid) {
701 DLIST_REMOVE(pvfs->open_files, f);
711 change the create options on an already open file
713 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
714 struct smbsrv_request *req,
715 struct pvfs_file *f, uint32_t create_options)
717 struct odb_lock *lck;
720 if (f->create_options == create_options) {
724 if ((f->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
725 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
726 return NT_STATUS_CANNOT_DELETE;
729 lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
731 return NT_STATUS_INTERNAL_DB_CORRUPTION;
734 status = odb_set_create_options(lck, f->fnum, create_options);
735 if (NT_STATUS_IS_OK(status)) {
736 f->create_options = create_options;
744 determine if a file can be deleted, or if it is prevented by an
747 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs, struct pvfs_filename *name)
752 status = pvfs_locking_key(name, name, &key);
753 if (!NT_STATUS_IS_OK(status)) {
754 return NT_STATUS_NO_MEMORY;
757 status = odb_can_open(pvfs->odb_context, &key,
758 NTCREATEX_SHARE_ACCESS_READ |
759 NTCREATEX_SHARE_ACCESS_WRITE |
760 NTCREATEX_SHARE_ACCESS_DELETE,
761 0, STD_RIGHT_DELETE_ACCESS);