r3463: separated out some more headers (asn_1.h, messages.h, dlinklist.h and ioctl.h)
[kai/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 #include "system/time.h"
26 #include "system/filesys.h"
27 #include "dlinklist.h"
28
29 /*
30   create file handles with convenient numbers for sniffers
31 */
32 #define PVFS_MIN_FILE_FNUM 0x100
33 #define PVFS_MIN_NEW_FNUM  0x200
34 #define PVFS_MIN_DIR_FNUM  0x300
35
36 /*
37   find open file handle given fnum
38 */
39 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
40                                struct smbsrv_request *req, uint16_t fnum)
41 {
42         struct pvfs_file *f;
43
44         f = idr_find(pvfs->idtree_fnum, fnum);
45         if (f == NULL) {
46                 return NULL;
47         }
48
49         if (req->session != f->session) {
50                 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n", 
51                          fnum));
52                 return NULL;
53         }
54
55         return f;
56 }
57
58
59 /*
60   cleanup a open directory handle
61 */
62 static int pvfs_dir_fd_destructor(void *p)
63 {
64         struct pvfs_file *f = p;
65         DLIST_REMOVE(f->pvfs->open_files, f);
66         idr_remove(f->pvfs->idtree_fnum, f->fnum);
67
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)));
72                 }
73         }
74
75         return 0;
76 }
77
78
79 /*
80   open a directory
81 */
82 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, 
83                                     struct smbsrv_request *req, 
84                                     struct pvfs_filename *name, 
85                                     union smb_open *io)
86 {
87         struct pvfs_file *f;
88         int fnum;
89         NTSTATUS status;
90         uint32_t create_action;
91
92         /* if the client says it must be a directory, and it isn't,
93            then fail */
94         if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
95                 return NT_STATUS_NOT_A_DIRECTORY;
96         }
97
98         switch (io->generic.in.open_disposition) {
99         case NTCREATEX_DISP_OPEN_IF:
100                 break;
101
102         case NTCREATEX_DISP_OPEN:
103                 if (!name->exists) {
104                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
105                 }
106                 break;
107
108         case NTCREATEX_DISP_CREATE:
109                 if (name->exists) {
110                         return NT_STATUS_OBJECT_NAME_COLLISION;
111                 }
112                 break;
113
114         case NTCREATEX_DISP_OVERWRITE_IF:
115         case NTCREATEX_DISP_OVERWRITE:
116         case NTCREATEX_DISP_SUPERSEDE:
117         default:
118                 return NT_STATUS_INVALID_PARAMETER;
119         }
120
121         f = talloc_p(req, struct pvfs_file);
122         if (f == NULL) {
123                 return NT_STATUS_NO_MEMORY;
124         }
125
126         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
127         if (fnum == -1) {
128                 talloc_free(f);
129                 return NT_STATUS_TOO_MANY_OPENED_FILES;
130         }
131
132         f->fnum = fnum;
133         f->fd = -1;
134         f->name = talloc_steal(f, name);
135         f->session = req->session;
136         f->smbpid = req->smbpid;
137         f->pvfs = pvfs;
138         f->pending_list = NULL;
139         f->lock_count = 0;
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;
143         f->seek_offset = 0;
144         f->position = 0;
145
146         DLIST_ADD(pvfs->open_files, f);
147
148         /* TODO: should we check in the opendb? Do directory opens 
149            follow the share_access rules? */
150
151
152         /* setup a destructor to avoid leaks on abnormal termination */
153         talloc_set_destructor(f, pvfs_dir_fd_destructor);
154
155         if (!name->exists) {
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);
160                 }
161                 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
162                                            PVFS_RESOLVE_NO_WILDCARD, &name);
163                 if (!NT_STATUS_IS_OK(status)) {
164                         return status;
165                 }
166                 create_action = NTCREATEX_ACTION_CREATED;
167         } else {
168                 create_action = NTCREATEX_ACTION_EXISTED;
169         }
170
171         if (!name->exists) {
172                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
173         }
174
175         /* the open succeeded, keep this handle permanently */
176         talloc_steal(pvfs, f);
177
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;
191
192         return NT_STATUS_OK;
193 }
194
195
196 /*
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)
200 */
201 static int pvfs_fd_destructor(void *p)
202 {
203         struct pvfs_file *f = p;
204         struct odb_lock *lck;
205         NTSTATUS status;
206
207         DLIST_REMOVE(f->pvfs->open_files, f);
208
209         pvfs_lock_close(f->pvfs, f);
210
211         if (f->fd != -1) {
212                 close(f->fd);
213                 f->fd = -1;
214         }
215
216         idr_remove(f->pvfs->idtree_fnum, f->fnum);
217
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)));
222                 }
223         }
224
225         lck = odb_lock(f, f->pvfs->odb_context, &f->locking_key);
226         if (lck == NULL) {
227                 DEBUG(0,("Unable to lock opendb for close\n"));
228                 return 0;
229         }
230
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)));
235         }
236
237         talloc_free(lck);
238
239         return 0;
240 }
241
242
243 /*
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
247 */
248 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name, 
249                                  TALLOC_CTX *mem_ctx, DATA_BLOB *key)
250 {
251         struct {
252                 dev_t device;
253                 ino_t inode;
254         } lock_context;
255         ZERO_STRUCT(lock_context);
256
257         lock_context.device = name->st.st_dev;
258         lock_context.inode = name->st.st_ino;
259
260         *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
261         if (key->data == NULL) {
262                 return NT_STATUS_NO_MEMORY;
263         }
264         
265         return NT_STATUS_OK;
266 }
267
268
269 /*
270   create a new file
271 */
272 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, 
273                                  struct smbsrv_request *req, 
274                                  struct pvfs_filename *name, 
275                                  union smb_open *io)
276 {
277         struct pvfs_file *f;
278         NTSTATUS status;
279         int flags, fnum, fd;
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;
284         mode_t mode;
285
286         if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
287             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
288                 return NT_STATUS_CANNOT_DELETE;
289         }
290         
291         if (access_mask & SEC_RIGHT_MAXIMUM_ALLOWED) {
292                 access_mask = GENERIC_RIGHTS_FILE_READ | GENERIC_RIGHTS_FILE_WRITE;
293         }
294
295         if ((access_mask & SA_RIGHT_FILE_READ_EXEC) &&
296             (access_mask & SA_RIGHT_FILE_WRITE_APPEND)) {
297                 flags = O_RDWR;
298         } else if (access_mask & SA_RIGHT_FILE_WRITE_APPEND) {
299                 flags = O_WRONLY;
300         } else {
301                 flags = O_RDONLY;
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 | FILE_ATTRIBUTE_ARCHIVE);
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         if ((access_mask & SA_RIGHT_FILE_READ_EXEC) &&
497             (access_mask & SA_RIGHT_FILE_WRITE_APPEND)) {
498                 flags |= O_RDWR;
499         } else if (access_mask & SA_RIGHT_FILE_WRITE_APPEND) {
500                 flags |= O_WRONLY;
501         } else {
502                 flags |= O_RDONLY;
503         }
504
505         /* handle creating a new file separately */
506         if (!name->exists) {
507                 status = pvfs_create_file(pvfs, req, name, io);
508                 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
509                         return status;
510                 }
511
512                 /* we've hit a race - the file was created during this call */
513                 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
514                         return status;
515                 }
516
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)) {
521                         return status;
522                 }
523                 /* fall through to a normal open */
524         }
525
526         if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
527             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
528                 return NT_STATUS_CANNOT_DELETE;
529         }
530
531         f = talloc_p(req, struct pvfs_file);
532         if (f == NULL) {
533                 return NT_STATUS_NO_MEMORY;
534         }
535
536         /* allocate a fnum */
537         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
538         if (fnum == -1) {
539                 return NT_STATUS_TOO_MANY_OPENED_FILES;
540         }
541
542         /* form the lock context used for byte range locking and
543            opendb locking */
544         status = pvfs_locking_key(name, f, &f->locking_key);
545         if (!NT_STATUS_IS_OK(status)) {
546                 idr_remove(pvfs->idtree_fnum, fnum);
547                 return status;
548         }
549
550         /* get a lock on this file before the actual open */
551         lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
552         if (lck == NULL) {
553                 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
554                          name->full_name));
555                 /* we were supposed to do a blocking lock, so something
556                    is badly wrong! */
557                 idr_remove(pvfs->idtree_fnum, fnum);
558                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
559         }
560
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);
565                 return status;
566         }
567
568         f->fnum = fnum;
569         f->fd = -1;
570         f->name = talloc_steal(f, name);
571         f->session = req->session;
572         f->smbpid = req->smbpid;
573         f->pvfs = pvfs;
574         f->pending_list = NULL;
575         f->lock_count = 0;
576         f->create_options = io->generic.in.create_options;
577         f->share_access = io->generic.in.share_access;
578         f->access_mask = access_mask;
579         f->seek_offset = 0;
580         f->position = 0;
581
582         DLIST_ADD(pvfs->open_files, f);
583
584         /* setup a destructor to avoid file descriptor leaks on
585            abnormal termination */
586         talloc_set_destructor(f, pvfs_fd_destructor);
587
588         /* do the actual open */
589         fd = open(name->full_name, flags);
590         if (fd == -1) {
591                 return pvfs_map_errno(pvfs, errno);
592         }
593
594         f->fd = fd;
595
596         /* re-resolve the open fd */
597         status = pvfs_resolve_name_fd(pvfs, fd, name);
598         if (!NT_STATUS_IS_OK(status)) {
599                 return status;
600         }
601
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;
615
616         /* success - keep the file handle */
617         talloc_steal(pvfs, f);
618
619         return NT_STATUS_OK;
620 }
621
622
623 /*
624   close a file
625 */
626 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
627                     struct smbsrv_request *req, union smb_close *io)
628 {
629         struct pvfs_state *pvfs = ntvfs->private_data;
630         struct pvfs_file *f;
631         NTSTATUS status;
632         struct utimbuf unix_times;
633
634         if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
635                 return NT_STATUS_UNSUCCESSFUL;
636         }
637
638         if (io->generic.level != RAW_CLOSE_CLOSE) {
639                 return ntvfs_map_close(req, io, ntvfs);
640         }
641
642         f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
643         if (!f) {
644                 return NT_STATUS_INVALID_HANDLE;
645         }
646
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);
651         }
652         
653         if (f->fd != -1 && 
654             close(f->fd) == -1) {
655                 status = pvfs_map_errno(pvfs, errno);
656         } else {
657                 status = NT_STATUS_OK;
658         }
659         f->fd = -1;
660
661         /* the destructor takes care of the rest */
662         talloc_free(f);
663
664         return status;
665 }
666
667
668 /*
669   logoff - close all file descriptors open by a vuid
670 */
671 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
672                      struct smbsrv_request *req)
673 {
674         struct pvfs_state *pvfs = ntvfs->private_data;
675         struct pvfs_file *f, *next;
676
677         for (f=pvfs->open_files;f;f=next) {
678                 next = f->next;
679                 if (f->session == req->session) {
680                         DLIST_REMOVE(pvfs->open_files, f);
681                         talloc_free(f);
682                 }
683         }
684
685         return NT_STATUS_OK;
686 }
687
688
689 /*
690   exit - close files for the current pid
691 */
692 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
693                    struct smbsrv_request *req)
694 {
695         struct pvfs_state *pvfs = ntvfs->private_data;
696         struct pvfs_file *f, *next;
697
698         for (f=pvfs->open_files;f;f=next) {
699                 next = f->next;
700                 if (f->smbpid == req->smbpid) {
701                         DLIST_REMOVE(pvfs->open_files, f);
702                         talloc_free(f);
703                 }
704         }
705
706         return NT_STATUS_OK;
707 }
708
709
710 /*
711   change the create options on an already open file
712 */
713 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
714                                     struct smbsrv_request *req, 
715                                     struct pvfs_file *f, uint32_t create_options)
716 {
717         struct odb_lock *lck;
718         NTSTATUS status;
719
720         if (f->create_options == create_options) {
721                 return NT_STATUS_OK;
722         }
723
724         if ((f->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
725             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
726                 return NT_STATUS_CANNOT_DELETE;
727         }
728
729         lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
730         if (lck == NULL) {
731                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
732         }
733
734         status = odb_set_create_options(lck, f->fnum, create_options);
735         if (NT_STATUS_IS_OK(status)) {
736                 f->create_options = create_options;
737         }
738
739         return status;
740 }
741
742
743 /*
744   determine if a file can be deleted, or if it is prevented by an
745   already open file
746 */
747 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs, struct pvfs_filename *name)
748 {
749         NTSTATUS status;
750         DATA_BLOB key;
751
752         status = pvfs_locking_key(name, name, &key);
753         if (!NT_STATUS_IS_OK(status)) {
754                 return NT_STATUS_NO_MEMORY;
755         }
756
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);
762
763         return status;
764 }