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"
27 create file handles with convenient numbers for sniffers
29 #define PVFS_MIN_FILE_FNUM 0x100
30 #define PVFS_MIN_NEW_FNUM 0x200
31 #define PVFS_MIN_DIR_FNUM 0x1000
34 find open file handle given fnum
36 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
37 struct smbsrv_request *req, uint16_t fnum)
41 f = idr_find(pvfs->idtree_fnum, fnum);
46 if (req->session != f->session) {
47 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n",
57 cleanup a open directory handle
59 static int pvfs_dir_fd_destructor(void *p)
61 struct pvfs_file *f = p;
62 DLIST_REMOVE(f->pvfs->open_files, f);
63 idr_remove(f->pvfs->idtree_fnum, f->fnum);
65 if (f->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
66 if (rmdir(f->name->full_name) != 0) {
67 DEBUG(0,("pvfs_close: failed to rmdir '%s'\n",
79 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
80 struct smbsrv_request *req,
81 struct pvfs_filename *name,
87 uint32_t create_action;
89 /* if the client says it must be a directory, and it isn't,
91 if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
92 return NT_STATUS_NOT_A_DIRECTORY;
95 switch (io->generic.in.open_disposition) {
96 case NTCREATEX_DISP_OPEN_IF:
99 case NTCREATEX_DISP_OPEN:
101 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
105 case NTCREATEX_DISP_CREATE:
107 return NT_STATUS_OBJECT_NAME_COLLISION;
111 case NTCREATEX_DISP_OVERWRITE_IF:
112 case NTCREATEX_DISP_OVERWRITE:
113 case NTCREATEX_DISP_SUPERSEDE:
115 return NT_STATUS_INVALID_PARAMETER;
118 f = talloc_p(req, struct pvfs_file);
120 return NT_STATUS_NO_MEMORY;
123 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
126 return NT_STATUS_TOO_MANY_OPENED_FILES;
131 f->name = talloc_steal(f, name);
132 f->session = req->session;
133 f->smbpid = req->smbpid;
135 f->pending_list = NULL;
137 f->locking_key = data_blob(NULL, 0);
138 f->create_options = io->generic.in.create_options;
139 f->share_access = io->generic.in.share_access;
143 DLIST_ADD(pvfs->open_files, f);
145 /* TODO: should we check in the opendb? Do directory opens
146 follow the share_access rules? */
149 /* setup a destructor to avoid leaks on abnormal termination */
150 talloc_set_destructor(f, pvfs_dir_fd_destructor);
153 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
154 mode_t mode = pvfs_fileperms(pvfs, attrib);
155 if (mkdir(name->full_name, mode) == -1) {
156 return pvfs_map_errno(pvfs,errno);
158 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
159 PVFS_RESOLVE_NO_WILDCARD, &name);
160 if (!NT_STATUS_IS_OK(status)) {
163 create_action = NTCREATEX_ACTION_CREATED;
165 create_action = NTCREATEX_ACTION_EXISTED;
169 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
172 /* the open succeeded, keep this handle permanently */
173 talloc_steal(pvfs, f);
175 io->generic.out.oplock_level = NO_OPLOCK;
176 io->generic.out.fnum = f->fnum;
177 io->generic.out.create_action = create_action;
178 io->generic.out.create_time = name->dos.create_time;
179 io->generic.out.access_time = name->dos.access_time;
180 io->generic.out.write_time = name->dos.write_time;
181 io->generic.out.change_time = name->dos.change_time;
182 io->generic.out.attrib = name->dos.attrib;
183 io->generic.out.alloc_size = name->dos.alloc_size;
184 io->generic.out.size = name->st.st_size;
185 io->generic.out.file_type = FILE_TYPE_DISK;
186 io->generic.out.ipc_state = 0;
187 io->generic.out.is_directory = 1;
194 by using a destructor we make sure that abnormal cleanup will not
195 leak file descriptors (assuming at least the top level pointer is freed, which
196 will cascade down to here)
198 static int pvfs_fd_destructor(void *p)
200 struct pvfs_file *f = p;
201 struct odb_lock *lck;
204 DLIST_REMOVE(f->pvfs->open_files, f);
206 pvfs_lock_close(f->pvfs, f);
213 idr_remove(f->pvfs->idtree_fnum, f->fnum);
215 if (f->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
216 if (unlink(f->name->full_name) != 0) {
217 DEBUG(0,("pvfs_close: failed to delete '%s'\n",
218 f->name->full_name));
222 lck = odb_lock(f, f->pvfs->odb_context, &f->locking_key);
224 DEBUG(0,("Unable to lock opendb for close\n"));
228 status = odb_close_file(lck, f->fnum);
229 if (!NT_STATUS_IS_OK(status)) {
230 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
231 f->name->full_name, nt_errstr(status)));
241 form the lock context used for byte range locking and opendb
242 locking. Note that we must zero here to take account of
243 possible padding on some architectures
245 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
246 TALLOC_CTX *mem_ctx, DATA_BLOB *key)
252 ZERO_STRUCT(lock_context);
254 lock_context.device = name->st.st_dev;
255 lock_context.inode = name->st.st_ino;
257 *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
258 if (key->data == NULL) {
259 return NT_STATUS_NO_MEMORY;
269 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
270 struct smbsrv_request *req,
271 struct pvfs_filename *name,
277 struct odb_lock *lck;
278 uint32_t create_options = io->generic.in.create_options;
279 uint32_t share_access = io->generic.in.share_access;
280 uint32_t access_mask = io->generic.in.access_mask;
283 if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
284 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
285 return NT_STATUS_CANNOT_DELETE;
288 if (access_mask & SEC_RIGHT_MAXIMUM_ALLOWED) {
289 access_mask = GENERIC_RIGHTS_FILE_READ | GENERIC_RIGHTS_FILE_WRITE;
292 switch (access_mask & (SA_RIGHT_FILE_READ_DATA | SA_RIGHT_FILE_WRITE_DATA)) {
293 case SA_RIGHT_FILE_READ_DATA:
296 case SA_RIGHT_FILE_WRITE_DATA:
299 case SA_RIGHT_FILE_WRITE_DATA|SA_RIGHT_FILE_READ_DATA:
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);
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 switch (access_mask & (SA_RIGHT_FILE_READ_DATA | SA_RIGHT_FILE_WRITE_DATA)) {
497 case SA_RIGHT_FILE_READ_DATA:
500 case SA_RIGHT_FILE_WRITE_DATA:
503 case SA_RIGHT_FILE_WRITE_DATA|SA_RIGHT_FILE_READ_DATA:
508 /* handle creating a new file separately */
510 status = pvfs_create_file(pvfs, req, name, io);
511 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
515 /* we've hit a race - the file was created during this call */
516 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
520 /* try re-resolving the name */
521 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
522 PVFS_RESOLVE_NO_WILDCARD, &name);
523 if (!NT_STATUS_IS_OK(status)) {
526 /* fall through to a normal open */
529 if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
530 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
531 return NT_STATUS_CANNOT_DELETE;
534 f = talloc_p(req, struct pvfs_file);
536 return NT_STATUS_NO_MEMORY;
539 /* allocate a fnum */
540 fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
542 return NT_STATUS_TOO_MANY_OPENED_FILES;
545 /* form the lock context used for byte range locking and
547 status = pvfs_locking_key(name, f, &f->locking_key);
548 if (!NT_STATUS_IS_OK(status)) {
549 idr_remove(pvfs->idtree_fnum, fnum);
553 /* get a lock on this file before the actual open */
554 lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
556 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
558 /* we were supposed to do a blocking lock, so something
560 idr_remove(pvfs->idtree_fnum, fnum);
561 return NT_STATUS_INTERNAL_DB_CORRUPTION;
564 /* see if we are allowed to open at the same time as existing opens */
565 status = odb_open_file(lck, fnum, share_access, create_options, access_mask);
566 if (!NT_STATUS_IS_OK(status)) {
567 idr_remove(pvfs->idtree_fnum, fnum);
573 f->name = talloc_steal(f, name);
574 f->session = req->session;
575 f->smbpid = req->smbpid;
577 f->pending_list = NULL;
579 f->create_options = io->generic.in.create_options;
580 f->share_access = io->generic.in.share_access;
581 f->access_mask = access_mask;
585 DLIST_ADD(pvfs->open_files, f);
587 /* setup a destructor to avoid file descriptor leaks on
588 abnormal termination */
589 talloc_set_destructor(f, pvfs_fd_destructor);
591 /* do the actual open */
592 fd = open(name->full_name, flags);
594 return pvfs_map_errno(pvfs, errno);
599 /* re-resolve the open fd */
600 status = pvfs_resolve_name_fd(pvfs, fd, name);
601 if (!NT_STATUS_IS_OK(status)) {
605 io->generic.out.oplock_level = NO_OPLOCK;
606 io->generic.out.fnum = f->fnum;
607 io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
608 io->generic.out.create_time = name->dos.create_time;
609 io->generic.out.access_time = name->dos.access_time;
610 io->generic.out.write_time = name->dos.write_time;
611 io->generic.out.change_time = name->dos.change_time;
612 io->generic.out.attrib = name->dos.attrib;
613 io->generic.out.alloc_size = name->dos.alloc_size;
614 io->generic.out.size = name->st.st_size;
615 io->generic.out.file_type = FILE_TYPE_DISK;
616 io->generic.out.ipc_state = 0;
617 io->generic.out.is_directory = 0;
619 /* success - keep the file handle */
620 talloc_steal(pvfs, f);
629 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
630 struct smbsrv_request *req, union smb_close *io)
632 struct pvfs_state *pvfs = ntvfs->private_data;
635 struct utimbuf unix_times;
637 if (io->generic.level != RAW_CLOSE_CLOSE) {
638 return ntvfs_map_close(req, io, ntvfs);
641 f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
643 return NT_STATUS_INVALID_HANDLE;
646 unix_times.actime = 0;
647 unix_times.modtime = io->close.in.write_time;
648 utime(f->name->full_name, &unix_times);
651 close(f->fd) == -1) {
652 status = pvfs_map_errno(pvfs, errno);
654 status = NT_STATUS_OK;
658 /* the destructor takes care of the rest */
666 logoff - close all file descriptors open by a vuid
668 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
669 struct smbsrv_request *req)
671 struct pvfs_state *pvfs = ntvfs->private_data;
672 struct pvfs_file *f, *next;
674 for (f=pvfs->open_files;f;f=next) {
676 if (f->session == req->session) {
677 DLIST_REMOVE(pvfs->open_files, f);
687 exit - close files for the current pid
689 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
690 struct smbsrv_request *req)
692 struct pvfs_state *pvfs = ntvfs->private_data;
693 struct pvfs_file *f, *next;
695 for (f=pvfs->open_files;f;f=next) {
697 if (f->smbpid == req->smbpid) {
698 DLIST_REMOVE(pvfs->open_files, f);
708 change the create options on an already open file
710 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
711 struct smbsrv_request *req,
712 struct pvfs_file *f, uint32_t create_options)
714 struct odb_lock *lck;
717 if (f->create_options == create_options) {
721 if ((f->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
722 (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
723 return NT_STATUS_CANNOT_DELETE;
726 lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
728 return NT_STATUS_INTERNAL_DB_CORRUPTION;
731 status = odb_set_create_options(lck, f->fnum, create_options);
732 if (NT_STATUS_IS_OK(status)) {
733 f->create_options = create_options;
741 determine if a file can be deleted, or if it is prevented by an
744 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs, struct pvfs_filename *name)
749 status = pvfs_locking_key(name, name, &key);
750 if (!NT_STATUS_IS_OK(status)) {
751 return NT_STATUS_NO_MEMORY;
754 status = odb_can_open(pvfs->odb_context, &key,
755 NTCREATEX_SHARE_ACCESS_READ |
756 NTCREATEX_SHARE_ACCESS_WRITE |
757 NTCREATEX_SHARE_ACCESS_DELETE,
758 0, STD_RIGHT_DELETE_ACCESS);