r3200: - improved the accuracy of openx emulation. We now nearly pass the openx porti...
[samba.git] / source / ntvfs / posix / pvfs_open.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    POSIX NTVFS backend - open and close
5
6    Copyright (C) Andrew Tridgell 2004
7
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.
12    
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.
17    
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.
21 */
22
23 #include "include/includes.h"
24 #include "vfs_posix.h"
25
26 /*
27   create file handles with convenient numbers for sniffers
28 */
29 #define PVFS_MIN_FILE_FNUM 0x100
30 #define PVFS_MIN_NEW_FNUM 0x200
31 #define PVFS_MIN_DIR_FNUM 0x1000
32
33 /*
34   find open file handle given fnum
35 */
36 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
37                                struct smbsrv_request *req, uint16_t fnum)
38 {
39         struct pvfs_file *f;
40
41         f = idr_find(pvfs->idtree_fnum, fnum);
42         if (f == NULL) {
43                 return NULL;
44         }
45
46         if (req->session != f->session) {
47                 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n", 
48                          fnum));
49                 return NULL;
50         }
51
52         return f;
53 }
54
55
56 /*
57   cleanup a open directory handle
58 */
59 static int pvfs_dir_fd_destructor(void *p)
60 {
61         struct pvfs_file *f = p;
62         DLIST_REMOVE(f->pvfs->open_files, f);
63         idr_remove(f->pvfs->idtree_fnum, f->fnum);
64
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", 
68                                  f->name->full_name));
69                 }
70         }
71
72         return 0;
73 }
74
75
76 /*
77   open a directory
78 */
79 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, 
80                                     struct smbsrv_request *req, 
81                                     struct pvfs_filename *name, 
82                                     union smb_open *io)
83 {
84         struct pvfs_file *f;
85         int fnum;
86         NTSTATUS status;
87         uint32_t create_action;
88
89         /* if the client says it must be a directory, and it isn't,
90            then fail */
91         if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
92                 return NT_STATUS_NOT_A_DIRECTORY;
93         }
94
95         switch (io->generic.in.open_disposition) {
96         case NTCREATEX_DISP_OPEN_IF:
97                 break;
98
99         case NTCREATEX_DISP_OPEN:
100                 if (!name->exists) {
101                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
102                 }
103                 break;
104
105         case NTCREATEX_DISP_CREATE:
106                 if (name->exists) {
107                         return NT_STATUS_OBJECT_NAME_COLLISION;
108                 }
109                 break;
110
111         case NTCREATEX_DISP_OVERWRITE_IF:
112         case NTCREATEX_DISP_OVERWRITE:
113         case NTCREATEX_DISP_SUPERSEDE:
114         default:
115                 return NT_STATUS_INVALID_PARAMETER;
116         }
117
118         f = talloc_p(req, struct pvfs_file);
119         if (f == NULL) {
120                 return NT_STATUS_NO_MEMORY;
121         }
122
123         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
124         if (fnum == -1) {
125                 talloc_free(f);
126                 return NT_STATUS_TOO_MANY_OPENED_FILES;
127         }
128
129         f->fnum = fnum;
130         f->fd = -1;
131         f->name = talloc_steal(f, name);
132         f->session = req->session;
133         f->smbpid = req->smbpid;
134         f->pvfs = pvfs;
135         f->pending_list = NULL;
136         f->lock_count = 0;
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;
140         f->seek_offset = 0;
141         f->position = 0;
142
143         DLIST_ADD(pvfs->open_files, f);
144
145         /* TODO: should we check in the opendb? Do directory opens 
146            follow the share_access rules? */
147
148
149         /* setup a destructor to avoid leaks on abnormal termination */
150         talloc_set_destructor(f, pvfs_dir_fd_destructor);
151
152         if (!name->exists) {
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);
157                 }
158                 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
159                                            PVFS_RESOLVE_NO_WILDCARD, &name);
160                 if (!NT_STATUS_IS_OK(status)) {
161                         return status;
162                 }
163                 create_action = NTCREATEX_ACTION_CREATED;
164         } else {
165                 create_action = NTCREATEX_ACTION_EXISTED;
166         }
167
168         if (!name->exists) {
169                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
170         }
171
172         /* the open succeeded, keep this handle permanently */
173         talloc_steal(pvfs, f);
174
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;
188
189         return NT_STATUS_OK;
190 }
191
192
193 /*
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)
197 */
198 static int pvfs_fd_destructor(void *p)
199 {
200         struct pvfs_file *f = p;
201         struct odb_lock *lck;
202         NTSTATUS status;
203
204         DLIST_REMOVE(f->pvfs->open_files, f);
205
206         pvfs_lock_close(f->pvfs, f);
207
208         if (f->fd != -1) {
209                 close(f->fd);
210                 f->fd = -1;
211         }
212
213         idr_remove(f->pvfs->idtree_fnum, f->fnum);
214
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));
219                 }
220         }
221
222         lck = odb_lock(f, f->pvfs->odb_context, &f->locking_key);
223         if (lck == NULL) {
224                 DEBUG(0,("Unable to lock opendb for close\n"));
225                 return 0;
226         }
227
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)));
232         }
233
234         talloc_free(lck);
235
236         return 0;
237 }
238
239
240 /*
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
244 */
245 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name, 
246                                  TALLOC_CTX *mem_ctx, DATA_BLOB *key)
247 {
248         struct {
249                 dev_t device;
250                 ino_t inode;
251         } lock_context;
252         ZERO_STRUCT(lock_context);
253
254         lock_context.device = name->st.st_dev;
255         lock_context.inode = name->st.st_ino;
256
257         *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
258         if (key->data == NULL) {
259                 return NT_STATUS_NO_MEMORY;
260         }
261         
262         return NT_STATUS_OK;
263 }
264
265
266 /*
267   create a new file
268 */
269 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, 
270                                  struct smbsrv_request *req, 
271                                  struct pvfs_filename *name, 
272                                  union smb_open *io)
273 {
274         struct pvfs_file *f;
275         NTSTATUS status;
276         int flags, fnum, fd;
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;
281         mode_t mode;
282
283         if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
284             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
285                 return NT_STATUS_CANNOT_DELETE;
286         }
287         
288         if (access_mask & SEC_RIGHT_MAXIMUM_ALLOWED) {
289                 access_mask = GENERIC_RIGHTS_FILE_READ | GENERIC_RIGHTS_FILE_WRITE;
290         }
291
292         switch (access_mask & (SA_RIGHT_FILE_READ_DATA | SA_RIGHT_FILE_WRITE_DATA)) {
293         case SA_RIGHT_FILE_READ_DATA:
294                 flags = O_RDONLY;
295                 break;
296         case SA_RIGHT_FILE_WRITE_DATA:
297                 flags = O_WRONLY;
298                 break;
299         case SA_RIGHT_FILE_WRITE_DATA|SA_RIGHT_FILE_READ_DATA:
300                 flags = O_RDWR;
301                 break;
302         }
303
304         f = talloc_p(req, struct pvfs_file);
305         if (f == NULL) {
306                 return NT_STATUS_NO_MEMORY;
307         }
308
309         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
310         if (fnum == -1) {
311                 return NT_STATUS_TOO_MANY_OPENED_FILES;
312         }
313
314         mode = pvfs_fileperms(pvfs, io->ntcreatex.in.file_attr);
315
316         /* create the file */
317         fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
318         if (fd == -1) {
319                 idr_remove(pvfs->idtree_fnum, fnum);
320                 return pvfs_map_errno(pvfs, errno);
321         }
322
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);
327                 close(fd);
328                 return status;
329         }
330
331         /* form the lock context used for byte range locking and
332            opendb locking */
333         status = pvfs_locking_key(name, f, &f->locking_key);
334         if (!NT_STATUS_IS_OK(status)) {
335                 idr_remove(pvfs->idtree_fnum, fnum);
336                 close(fd);
337                 return status;
338         }
339
340         /* grab a lock on the open file record */
341         lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
342         if (lck == NULL) {
343                 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
344                          name->full_name));
345                 /* we were supposed to do a blocking lock, so something
346                    is badly wrong! */
347                 idr_remove(pvfs->idtree_fnum, fnum);
348                 close(fd);
349                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
350         }
351
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);
356                 close(fd);
357                 return status;
358         }
359
360         f->fnum = fnum;
361         f->fd = fd;
362         f->name = talloc_steal(f, name);
363         f->session = req->session;
364         f->smbpid = req->smbpid;
365         f->pvfs = pvfs;
366         f->pending_list = NULL;
367         f->lock_count = 0;
368         f->create_options = io->generic.in.create_options;
369         f->share_access = io->generic.in.share_access;
370         f->access_mask = access_mask;
371         f->seek_offset = 0;
372         f->position = 0;
373
374         DLIST_ADD(pvfs->open_files, f);
375
376         /* setup a destructor to avoid file descriptor leaks on
377            abnormal termination */
378         talloc_set_destructor(f, pvfs_fd_destructor);
379
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;
393
394         /* success - keep the file handle */
395         talloc_steal(pvfs, f);
396
397         return NT_STATUS_OK;
398 }
399
400
401 /*
402   open a file
403 */
404 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
405                    struct smbsrv_request *req, union smb_open *io)
406 {
407         struct pvfs_state *pvfs = ntvfs->private_data;
408         int fd, flags;
409         struct pvfs_filename *name;
410         struct pvfs_file *f;
411         NTSTATUS status;
412         int fnum;
413         struct odb_lock *lck;
414         uint32_t create_options;
415         uint32_t share_access;
416         uint32_t access_mask;
417
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
422            change eventually */    
423         if (io->generic.level != RAW_OPEN_GENERIC) {
424                 return ntvfs_map_open(req, io, ntvfs);
425         }
426
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)) {
431                 return status;
432         }
433
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);
438         }
439
440         create_options = io->generic.in.create_options;
441         share_access   = io->generic.in.share_access;
442         access_mask    = io->generic.in.access_mask;
443
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;
447                 } else {
448                         access_mask = GENERIC_RIGHTS_FILE_READ | GENERIC_RIGHTS_FILE_WRITE;
449                 }
450         }
451
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;
456         }
457
458         switch (io->generic.in.open_disposition) {
459         case NTCREATEX_DISP_SUPERSEDE:
460                 flags = O_TRUNC;
461                 break;
462
463         case NTCREATEX_DISP_OVERWRITE_IF:
464                 flags = O_TRUNC;
465                 break;
466
467         case NTCREATEX_DISP_OPEN:
468                 if (!name->exists) {
469                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
470                 }
471                 flags = 0;
472                 break;
473
474         case NTCREATEX_DISP_OVERWRITE:
475                 if (!name->exists) {
476                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
477                 }
478                 flags = O_TRUNC;
479                 break;
480
481         case NTCREATEX_DISP_CREATE:
482                 if (name->exists) {
483                         return NT_STATUS_OBJECT_NAME_COLLISION;
484                 }
485                 flags = 0;
486                 break;
487
488         case NTCREATEX_DISP_OPEN_IF:
489                 flags = 0;
490                 break;
491
492         default:
493                 return NT_STATUS_INVALID_PARAMETER;
494         }
495
496         switch (access_mask & (SA_RIGHT_FILE_READ_DATA | SA_RIGHT_FILE_WRITE_DATA)) {
497         case SA_RIGHT_FILE_READ_DATA:
498                 flags |= O_RDONLY;
499                 break;
500         case SA_RIGHT_FILE_WRITE_DATA:
501                 flags |= O_WRONLY;
502                 break;
503         case SA_RIGHT_FILE_WRITE_DATA|SA_RIGHT_FILE_READ_DATA:
504                 flags |= O_RDWR;
505                 break;
506         }
507
508         /* handle creating a new file separately */
509         if (!name->exists) {
510                 status = pvfs_create_file(pvfs, req, name, io);
511                 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
512                         return status;
513                 }
514
515                 /* we've hit a race - the file was created during this call */
516                 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
517                         return status;
518                 }
519
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)) {
524                         return status;
525                 }
526                 /* fall through to a normal open */
527         }
528
529         if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
530             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
531                 return NT_STATUS_CANNOT_DELETE;
532         }
533
534         f = talloc_p(req, struct pvfs_file);
535         if (f == NULL) {
536                 return NT_STATUS_NO_MEMORY;
537         }
538
539         /* allocate a fnum */
540         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
541         if (fnum == -1) {
542                 return NT_STATUS_TOO_MANY_OPENED_FILES;
543         }
544
545         /* form the lock context used for byte range locking and
546            opendb locking */
547         status = pvfs_locking_key(name, f, &f->locking_key);
548         if (!NT_STATUS_IS_OK(status)) {
549                 idr_remove(pvfs->idtree_fnum, fnum);
550                 return status;
551         }
552
553         /* get a lock on this file before the actual open */
554         lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
555         if (lck == NULL) {
556                 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
557                          name->full_name));
558                 /* we were supposed to do a blocking lock, so something
559                    is badly wrong! */
560                 idr_remove(pvfs->idtree_fnum, fnum);
561                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
562         }
563
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);
568                 return status;
569         }
570
571         f->fnum = fnum;
572         f->fd = -1;
573         f->name = talloc_steal(f, name);
574         f->session = req->session;
575         f->smbpid = req->smbpid;
576         f->pvfs = pvfs;
577         f->pending_list = NULL;
578         f->lock_count = 0;
579         f->create_options = io->generic.in.create_options;
580         f->share_access = io->generic.in.share_access;
581         f->access_mask = access_mask;
582         f->seek_offset = 0;
583         f->position = 0;
584
585         DLIST_ADD(pvfs->open_files, f);
586
587         /* setup a destructor to avoid file descriptor leaks on
588            abnormal termination */
589         talloc_set_destructor(f, pvfs_fd_destructor);
590
591         /* do the actual open */
592         fd = open(name->full_name, flags);
593         if (fd == -1) {
594                 return pvfs_map_errno(pvfs, errno);
595         }
596
597         f->fd = fd;
598
599         /* re-resolve the open fd */
600         status = pvfs_resolve_name_fd(pvfs, fd, name);
601         if (!NT_STATUS_IS_OK(status)) {
602                 return status;
603         }
604
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;
618
619         /* success - keep the file handle */
620         talloc_steal(pvfs, f);
621
622         return NT_STATUS_OK;
623 }
624
625
626 /*
627   close a file
628 */
629 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
630                     struct smbsrv_request *req, union smb_close *io)
631 {
632         struct pvfs_state *pvfs = ntvfs->private_data;
633         struct pvfs_file *f;
634         NTSTATUS status;
635         struct utimbuf unix_times;
636
637         if (io->generic.level != RAW_CLOSE_CLOSE) {
638                 return ntvfs_map_close(req, io, ntvfs);
639         }
640
641         f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
642         if (!f) {
643                 return NT_STATUS_INVALID_HANDLE;
644         }
645
646         unix_times.actime = 0;
647         unix_times.modtime = io->close.in.write_time;
648         utime(f->name->full_name, &unix_times);
649         
650         if (f->fd != -1 && 
651             close(f->fd) == -1) {
652                 status = pvfs_map_errno(pvfs, errno);
653         } else {
654                 status = NT_STATUS_OK;
655         }
656         f->fd = -1;
657
658         /* the destructor takes care of the rest */
659         talloc_free(f);
660
661         return status;
662 }
663
664
665 /*
666   logoff - close all file descriptors open by a vuid
667 */
668 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
669                      struct smbsrv_request *req)
670 {
671         struct pvfs_state *pvfs = ntvfs->private_data;
672         struct pvfs_file *f, *next;
673
674         for (f=pvfs->open_files;f;f=next) {
675                 next = f->next;
676                 if (f->session == req->session) {
677                         DLIST_REMOVE(pvfs->open_files, f);
678                         talloc_free(f);
679                 }
680         }
681
682         return NT_STATUS_OK;
683 }
684
685
686 /*
687   exit - close files for the current pid
688 */
689 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
690                    struct smbsrv_request *req)
691 {
692         struct pvfs_state *pvfs = ntvfs->private_data;
693         struct pvfs_file *f, *next;
694
695         for (f=pvfs->open_files;f;f=next) {
696                 next = f->next;
697                 if (f->smbpid == req->smbpid) {
698                         DLIST_REMOVE(pvfs->open_files, f);
699                         talloc_free(f);
700                 }
701         }
702
703         return NT_STATUS_OK;
704 }
705
706
707 /*
708   change the create options on an already open file
709 */
710 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
711                                     struct smbsrv_request *req, 
712                                     struct pvfs_file *f, uint32_t create_options)
713 {
714         struct odb_lock *lck;
715         NTSTATUS status;
716
717         if (f->create_options == create_options) {
718                 return NT_STATUS_OK;
719         }
720
721         if ((f->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
722             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
723                 return NT_STATUS_CANNOT_DELETE;
724         }
725
726         lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
727         if (lck == NULL) {
728                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
729         }
730
731         status = odb_set_create_options(lck, f->fnum, create_options);
732         if (NT_STATUS_IS_OK(status)) {
733                 f->create_options = create_options;
734         }
735
736         return status;
737 }
738
739
740 /*
741   determine if a file can be deleted, or if it is prevented by an
742   already open file
743 */
744 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs, struct pvfs_filename *name)
745 {
746         NTSTATUS status;
747         DATA_BLOB key;
748
749         status = pvfs_locking_key(name, name, &key);
750         if (!NT_STATUS_IS_OK(status)) {
751                 return NT_STATUS_NO_MEMORY;
752         }
753
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);
759
760         return status;
761 }