r14793: the RAW-NOTIFY test now passes. Next I need to make it efficient, and
[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 "includes.h"
24 #include "vfs_posix.h"
25 #include "system/dir.h"
26 #include "system/time.h"
27 #include "dlinklist.h"
28 #include "messaging/messaging.h"
29 #include "librpc/gen_ndr/xattr.h"
30
31 /*
32   create file handles with convenient numbers for sniffers
33 */
34 #define PVFS_MIN_FILE_FNUM 0x100
35 #define PVFS_MIN_NEW_FNUM  0x200
36 #define PVFS_MIN_DIR_FNUM  0x300
37
38 /*
39   find open file handle given fnum
40 */
41 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
42                                struct ntvfs_request *req, uint16_t fnum)
43 {
44         struct pvfs_file *f;
45
46         f = idr_find(pvfs->files.idtree, fnum);
47         if (f == NULL) {
48                 return NULL;
49         }
50
51         if (f->fnum != fnum) {
52                 smb_panic("pvfs_find_fd: idtree_fnum corruption\n");
53         }
54
55         if (req->session_info != f->session_info) {
56                 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n", 
57                          fnum));
58                 return NULL;
59         }
60
61         return f;
62 }
63
64
65 /*
66   cleanup a open directory handle
67 */
68 static int pvfs_dir_handle_destructor(void *p)
69 {
70         struct pvfs_file_handle *h = p;
71         int open_count;
72         char *path;
73
74         if (h->name->stream_name == NULL && 
75             pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
76             open_count == 1) {
77                 NTSTATUS status;
78                 status = pvfs_xattr_unlink_hook(h->pvfs, path);
79                 if (!NT_STATUS_IS_OK(status)) {
80                         DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
81                                  path, nt_errstr(status)));
82                 }
83                 if (rmdir(path) != 0) {
84                         DEBUG(0,("pvfs_dir_handle_destructor: failed to rmdir '%s' - %s\n", 
85                                  path, strerror(errno)));
86                 }
87         }
88
89         if (h->have_opendb_entry) {
90                 struct odb_lock *lck;
91                 NTSTATUS status;
92
93                 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
94                 if (lck == NULL) {
95                         DEBUG(0,("Unable to lock opendb for close\n"));
96                         return 0;
97                 }
98
99                 status = odb_close_file(lck, h);
100                 if (!NT_STATUS_IS_OK(status)) {
101                         DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n", 
102                                  h->name->full_name, nt_errstr(status)));
103                 }
104
105                 talloc_free(lck);
106         }
107
108         return 0;
109 }
110
111 /*
112   cleanup a open directory fnum
113 */
114 static int pvfs_dir_fnum_destructor(void *p)
115 {
116         struct pvfs_file *f = p;
117         DLIST_REMOVE(f->pvfs->files.list, f);
118         idr_remove(f->pvfs->files.idtree, f->fnum);
119         return 0;
120 }
121
122 /*
123   setup any EAs and the ACL on newly created files/directories
124 */
125 static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
126                                         struct ntvfs_request *req,
127                                         struct pvfs_filename *name,
128                                         int fd, int fnum,
129                                         union smb_open *io)
130 {
131         NTSTATUS status;
132
133         /* setup any EAs that were asked for */
134         if (io->ntcreatex.in.ea_list) {
135                 status = pvfs_setfileinfo_ea_set(pvfs, name, fd, 
136                                                  io->ntcreatex.in.ea_list->num_eas,
137                                                  io->ntcreatex.in.ea_list->eas);
138                 if (!NT_STATUS_IS_OK(status)) {
139                         return status;
140                 }
141         }
142
143         /* setup an initial sec_desc if requested */
144         if (io->ntcreatex.in.sec_desc) {
145                 union smb_setfileinfo set;
146
147                 set.set_secdesc.in.file.fnum = fnum;
148                 set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
149                 set.set_secdesc.in.sd = io->ntcreatex.in.sec_desc;
150
151                 status = pvfs_acl_set(pvfs, req, name, fd, SEC_STD_WRITE_DAC, &set);
152         } else {
153                 /* otherwise setup an inherited acl from the parent */
154                 status = pvfs_acl_inherit(pvfs, req, name, fd);
155         }
156
157         return status;
158 }
159
160 /*
161   form the lock context used for opendb locking. Note that we must
162   zero here to take account of possible padding on some architectures
163 */
164 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name, 
165                                  TALLOC_CTX *mem_ctx, DATA_BLOB *key)
166 {
167         struct {
168                 dev_t device;
169                 ino_t inode;
170         } lock_context;
171         ZERO_STRUCT(lock_context);
172
173         lock_context.device = name->st.st_dev;
174         lock_context.inode = name->st.st_ino;
175
176         *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
177         if (key->data == NULL) {
178                 return NT_STATUS_NO_MEMORY;
179         }
180         
181         return NT_STATUS_OK;
182 }
183
184
185 /*
186   open a directory
187 */
188 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, 
189                                     struct ntvfs_request *req, 
190                                     struct pvfs_filename *name, 
191                                     union smb_open *io)
192 {
193         struct pvfs_file *f;
194         int fnum;
195         NTSTATUS status;
196         uint32_t create_action;
197         uint32_t access_mask = io->generic.in.access_mask;
198         struct odb_lock *lck;
199         BOOL del_on_close;
200         uint32_t create_options;
201         uint32_t share_access;
202
203         create_options = io->generic.in.create_options;
204         share_access   = io->generic.in.share_access;
205
206         if (name->stream_name) {
207                 return NT_STATUS_NOT_A_DIRECTORY;
208         }
209
210         /* if the client says it must be a directory, and it isn't,
211            then fail */
212         if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
213                 return NT_STATUS_NOT_A_DIRECTORY;
214         }
215
216         switch (io->generic.in.open_disposition) {
217         case NTCREATEX_DISP_OPEN_IF:
218                 break;
219
220         case NTCREATEX_DISP_OPEN:
221                 if (!name->exists) {
222                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
223                 }
224                 break;
225
226         case NTCREATEX_DISP_CREATE:
227                 if (name->exists) {
228                         return NT_STATUS_OBJECT_NAME_COLLISION;
229                 }
230                 break;
231
232         case NTCREATEX_DISP_OVERWRITE_IF:
233         case NTCREATEX_DISP_OVERWRITE:
234         case NTCREATEX_DISP_SUPERSEDE:
235         default:
236                 return NT_STATUS_INVALID_PARAMETER;
237         }
238
239         f = talloc(req, struct pvfs_file);
240         if (f == NULL) {
241                 return NT_STATUS_NO_MEMORY;
242         }
243
244         f->handle = talloc(f, struct pvfs_file_handle);
245         if (f->handle == NULL) {
246                 return NT_STATUS_NO_MEMORY;
247         }
248
249         fnum = idr_get_new_above(pvfs->files.idtree, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
250         if (fnum == -1) {
251                 return NT_STATUS_TOO_MANY_OPENED_FILES;
252         }
253
254         if (name->exists) {
255                 /* check the security descriptor */
256                 status = pvfs_access_check(pvfs, req, name, &access_mask);
257         } else {
258                 status = pvfs_access_check_create(pvfs, req, name, &access_mask);
259         }
260         if (!NT_STATUS_IS_OK(status)) {
261                 idr_remove(pvfs->files.idtree, fnum);
262                 return status;
263         }
264
265         f->fnum          = fnum;
266         f->session_info  = req->session_info;
267         f->smbpid        = req->smbpid;
268         f->pvfs          = pvfs;
269         f->pending_list  = NULL;
270         f->lock_count    = 0;
271         f->share_access  = io->generic.in.share_access;
272         f->impersonation = io->generic.in.impersonation;
273         f->access_mask   = access_mask;
274         f->notify_buffer = NULL;
275
276         f->handle->pvfs              = pvfs;
277         f->handle->name              = talloc_steal(f->handle, name);
278         f->handle->fd                = -1;
279         f->handle->odb_locking_key   = data_blob(NULL, 0);
280         f->handle->brl_locking_key   = data_blob(NULL, 0);
281         f->handle->create_options    = io->generic.in.create_options;
282         f->handle->seek_offset       = 0;
283         f->handle->position          = 0;
284         f->handle->mode              = 0;
285         f->handle->sticky_write_time = False;
286
287         if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
288             pvfs_directory_empty(pvfs, f->handle->name)) {
289                 del_on_close = True;
290         } else {
291                 del_on_close = False;
292         }
293
294
295         if (name->exists) {
296                 /* form the lock context used for opendb locking */
297                 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
298                 if (!NT_STATUS_IS_OK(status)) {
299                         idr_remove(pvfs->files.idtree, f->fnum);
300                         return status;
301                 }
302
303                 /* get a lock on this file before the actual open */
304                 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
305                 if (lck == NULL) {
306                         DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
307                                  name->full_name));
308                         /* we were supposed to do a blocking lock, so something
309                            is badly wrong! */
310                         idr_remove(pvfs->files.idtree, fnum);
311                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
312                 }
313                 
314                 /* see if we are allowed to open at the same time as existing opens */
315                 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
316                                        share_access, access_mask, del_on_close, name->full_name);
317
318                 if (!NT_STATUS_IS_OK(status)) {
319                         idr_remove(pvfs->files.idtree, f->fnum);
320                         talloc_free(lck);
321                         return status;
322                 }
323
324                 f->handle->have_opendb_entry = True;
325         }
326
327         DLIST_ADD(pvfs->files.list, f);
328
329         /* setup destructors to avoid leaks on abnormal termination */
330         talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
331         talloc_set_destructor(f, pvfs_dir_fnum_destructor);
332
333         if (!name->exists) {
334                 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
335                 mode_t mode = pvfs_fileperms(pvfs, attrib);
336
337                 if (mkdir(name->full_name, mode) == -1) {
338                         idr_remove(pvfs->files.idtree, fnum);
339                         return pvfs_map_errno(pvfs,errno);
340                 }
341
342                 pvfs_xattr_unlink_hook(pvfs, name->full_name);
343
344                 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
345                 if (!NT_STATUS_IS_OK(status)) {
346                         goto cleanup_delete;
347                 }
348
349                 status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, fnum, io);
350                 if (!NT_STATUS_IS_OK(status)) {
351                         goto cleanup_delete;
352                 }
353
354                 /* form the lock context used for opendb locking */
355                 status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
356                 if (!NT_STATUS_IS_OK(status)) {
357                         idr_remove(pvfs->files.idtree, f->fnum);
358                         return status;
359                 }
360
361                 lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
362                 if (lck == NULL) {
363                         DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
364                                  name->full_name));
365                         /* we were supposed to do a blocking lock, so something
366                            is badly wrong! */
367                         idr_remove(pvfs->files.idtree, fnum);
368                         return NT_STATUS_INTERNAL_DB_CORRUPTION;
369                 }
370
371                 status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
372                                        share_access, access_mask, del_on_close, name->full_name);
373
374                 if (!NT_STATUS_IS_OK(status)) {
375                         goto cleanup_delete;
376                 }
377
378                 f->handle->have_opendb_entry = True;
379
380                 create_action = NTCREATEX_ACTION_CREATED;
381         } else {
382                 create_action = NTCREATEX_ACTION_EXISTED;
383         }
384
385         if (!name->exists) {
386                 idr_remove(pvfs->files.idtree, fnum);
387                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
388         }
389
390         /* the open succeeded, keep this handle permanently */
391         talloc_steal(pvfs, f);
392
393         io->generic.out.oplock_level  = OPLOCK_NONE;
394         io->generic.out.file.fnum     = f->fnum;
395         io->generic.out.create_action = create_action;
396         io->generic.out.create_time   = name->dos.create_time;
397         io->generic.out.access_time   = name->dos.access_time;
398         io->generic.out.write_time    = name->dos.write_time;
399         io->generic.out.change_time   = name->dos.change_time;
400         io->generic.out.attrib        = name->dos.attrib;
401         io->generic.out.alloc_size    = name->dos.alloc_size;
402         io->generic.out.size          = name->st.st_size;
403         io->generic.out.file_type     = FILE_TYPE_DISK;
404         io->generic.out.ipc_state     = 0;
405         io->generic.out.is_directory  = 1;
406
407         return NT_STATUS_OK;
408
409 cleanup_delete:
410         idr_remove(pvfs->files.idtree, fnum);
411         rmdir(name->full_name);
412         return status;
413 }
414
415 /*
416   destroy a struct pvfs_file_handle
417 */
418 static int pvfs_handle_destructor(void *p)
419 {
420         struct pvfs_file_handle *h = p;
421         int open_count;
422         char *path;
423
424         /* the write time is no longer sticky */
425         if (h->sticky_write_time) {
426                 NTSTATUS status;
427                 status = pvfs_dosattrib_load(h->pvfs, h->name, h->fd);
428                 if (NT_STATUS_IS_OK(status)) {
429                         h->name->dos.flags &= ~XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
430                         pvfs_dosattrib_save(h->pvfs, h->name, h->fd);
431                 }
432         }
433         
434         if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
435             h->name->stream_name) {
436                 NTSTATUS status;
437                 status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
438                 if (!NT_STATUS_IS_OK(status)) {
439                         DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
440                                  h->name->stream_name, h->name->full_name));
441                 }
442         }
443
444         if (h->fd != -1) {
445                 if (close(h->fd) != 0) {
446                         DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
447                                  h->fd, h->name->full_name, strerror(errno)));
448                 }
449                 h->fd = -1;
450         }
451
452         if (h->name->stream_name == NULL && 
453             pvfs_delete_on_close_set(h->pvfs, h, &open_count, &path) &&
454             open_count == 1) {
455                 NTSTATUS status;
456                 status = pvfs_xattr_unlink_hook(h->pvfs, path);
457                 if (!NT_STATUS_IS_OK(status)) {
458                         DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
459                                  path, nt_errstr(status)));
460                 }
461                 if (unlink(path) != 0) {
462                         DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n", 
463                                  path, strerror(errno)));
464                 }
465         }
466
467         if (h->have_opendb_entry) {
468                 struct odb_lock *lck;
469                 NTSTATUS status;
470
471                 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
472                 if (lck == NULL) {
473                         DEBUG(0,("Unable to lock opendb for close\n"));
474                         return 0;
475                 }
476
477                 status = odb_close_file(lck, h);
478                 if (!NT_STATUS_IS_OK(status)) {
479                         DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n", 
480                                  h->name->full_name, nt_errstr(status)));
481                 }
482
483                 talloc_free(lck);
484         }
485
486         return 0;
487 }
488
489
490 /*
491   destroy a struct pvfs_file
492 */
493 static int pvfs_fnum_destructor(void *p)
494 {
495         struct pvfs_file *f = p;
496
497         DLIST_REMOVE(f->pvfs->files.list, f);
498         pvfs_lock_close(f->pvfs, f);
499         idr_remove(f->pvfs->files.idtree, f->fnum);
500
501         return 0;
502 }
503
504
505 /*
506   form the lock context used for byte range locking. This is separate
507   from the locking key used for opendb locking as it needs to take
508   account of file streams (each stream is a separate byte range
509   locking space)
510 */
511 static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name, 
512                                      TALLOC_CTX *mem_ctx, DATA_BLOB *key)
513 {
514         DATA_BLOB odb_key;
515         NTSTATUS status;
516         status = pvfs_locking_key(name, mem_ctx, &odb_key);
517         if (!NT_STATUS_IS_OK(status)) {
518                 return status;
519         }
520         if (name->stream_name == NULL) {
521                 *key = odb_key;
522                 return NT_STATUS_OK;
523         }
524         *key = data_blob_talloc(mem_ctx, NULL, 
525                                 odb_key.length + strlen(name->stream_name) + 1);
526         if (key->data == NULL) {
527                 return NT_STATUS_NO_MEMORY;
528         }
529         memcpy(key->data, odb_key.data, odb_key.length);
530         memcpy(key->data + odb_key.length, 
531                name->stream_name, strlen(name->stream_name)+1);
532         data_blob_free(&odb_key);
533         return NT_STATUS_OK;
534 }
535
536
537 /*
538   create a new file
539 */
540 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, 
541                                  struct ntvfs_request *req, 
542                                  struct pvfs_filename *name, 
543                                  union smb_open *io)
544 {
545         struct pvfs_file *f;
546         NTSTATUS status;
547         int flags, fnum, fd;
548         struct odb_lock *lck;
549         uint32_t create_options = io->generic.in.create_options;
550         uint32_t share_access = io->generic.in.share_access;
551         uint32_t access_mask = io->generic.in.access_mask;
552         mode_t mode;
553         uint32_t attrib;
554         BOOL del_on_close;
555         struct pvfs_filename *parent;
556
557         if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
558             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
559                 return NT_STATUS_CANNOT_DELETE;
560         }
561         
562         status = pvfs_access_check_create(pvfs, req, name, &access_mask);
563         NT_STATUS_NOT_OK_RETURN(status);
564
565         /* check that the parent isn't opened with delete on close set */
566         status = pvfs_resolve_parent(pvfs, req, name, &parent);
567         if (NT_STATUS_IS_OK(status)) {
568                 DATA_BLOB locking_key;
569                 status = pvfs_locking_key(parent, req, &locking_key);
570                 NT_STATUS_NOT_OK_RETURN(status);
571                 status = odb_get_delete_on_close(pvfs->odb_context, &locking_key, 
572                                                  &del_on_close, NULL, NULL);
573                 NT_STATUS_NOT_OK_RETURN(status);
574                 if (del_on_close) {
575                         return NT_STATUS_DELETE_PENDING;
576                 }
577         }
578
579         if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
580                 flags = O_RDWR;
581         } else {
582                 flags = O_RDONLY;
583         }
584
585         f = talloc(req, struct pvfs_file);
586         if (f == NULL) {
587                 return NT_STATUS_NO_MEMORY;
588         }
589
590         f->handle = talloc(f, struct pvfs_file_handle);
591         if (f->handle == NULL) {
592                 return NT_STATUS_NO_MEMORY;
593         }
594
595         fnum = idr_get_new_above(pvfs->files.idtree, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
596         if (fnum == -1) {
597                 return NT_STATUS_TOO_MANY_OPENED_FILES;
598         }
599
600         attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
601         mode = pvfs_fileperms(pvfs, attrib);
602
603         /* create the file */
604         fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
605         if (fd == -1) {
606                 idr_remove(pvfs->files.idtree, fnum);
607                 return pvfs_map_errno(pvfs, errno);
608         }
609
610         pvfs_xattr_unlink_hook(pvfs, name->full_name);
611
612         /* if this was a stream create then create the stream as well */
613         if (name->stream_name) {
614                 status = pvfs_stream_create(pvfs, name, fd);
615                 if (!NT_STATUS_IS_OK(status)) {
616                         idr_remove(pvfs->files.idtree, fnum);
617                         close(fd);
618                         return status;
619                 }
620         }
621
622         /* re-resolve the open fd */
623         status = pvfs_resolve_name_fd(pvfs, fd, name);
624         if (!NT_STATUS_IS_OK(status)) {
625                 idr_remove(pvfs->files.idtree, fnum);
626                 close(fd);
627                 return status;
628         }
629
630         name->dos.attrib = attrib;
631         status = pvfs_dosattrib_save(pvfs, name, fd);
632         if (!NT_STATUS_IS_OK(status)) {
633                 goto cleanup_delete;
634         }
635
636
637         status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, fnum, io);
638         if (!NT_STATUS_IS_OK(status)) {
639                 goto cleanup_delete;
640         }
641
642         /* form the lock context used for byte range locking and
643            opendb locking */
644         status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
645         if (!NT_STATUS_IS_OK(status)) {
646                 goto cleanup_delete;
647         }
648
649         status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
650         if (!NT_STATUS_IS_OK(status)) {
651                 goto cleanup_delete;
652         }
653
654         /* grab a lock on the open file record */
655         lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
656         if (lck == NULL) {
657                 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
658                          name->full_name));
659                 /* we were supposed to do a blocking lock, so something
660                    is badly wrong! */
661                 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
662                 goto cleanup_delete;
663         }
664
665         if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
666                 del_on_close = True;
667         } else {
668                 del_on_close = False;
669         }
670
671         status = odb_open_file(lck, f->handle, name->stream_id,
672                                share_access, access_mask, del_on_close, name->full_name);
673         talloc_free(lck);
674         if (!NT_STATUS_IS_OK(status)) {
675                 /* bad news, we must have hit a race - we don't delete the file
676                    here as the most likely scenario is that someone else created 
677                    the file at the same time */
678                 idr_remove(pvfs->files.idtree, fnum);
679                 close(fd);
680                 return status;
681         }
682
683         f->fnum              = fnum;
684         f->session_info      = req->session_info;
685         f->smbpid            = req->smbpid;
686         f->pvfs              = pvfs;
687         f->pending_list      = NULL;
688         f->lock_count        = 0;
689         f->share_access      = io->generic.in.share_access;
690         f->access_mask       = access_mask;
691         f->impersonation     = io->generic.in.impersonation;
692         f->notify_buffer     = NULL;
693
694         f->handle->pvfs              = pvfs;
695         f->handle->name              = talloc_steal(f->handle, name);
696         f->handle->fd                = fd;
697         f->handle->create_options    = io->generic.in.create_options;
698         f->handle->seek_offset       = 0;
699         f->handle->position          = 0;
700         f->handle->mode              = 0;
701         f->handle->have_opendb_entry = True;
702         f->handle->sticky_write_time = False;
703
704         DLIST_ADD(pvfs->files.list, f);
705
706         /* setup a destructor to avoid file descriptor leaks on
707            abnormal termination */
708         talloc_set_destructor(f, pvfs_fnum_destructor);
709         talloc_set_destructor(f->handle, pvfs_handle_destructor);
710
711         
712         if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
713                 io->generic.out.oplock_level  = OPLOCK_BATCH;
714         } else {
715                 io->generic.out.oplock_level  = OPLOCK_NONE;
716         }
717         io->generic.out.file.fnum     = f->fnum;
718         io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
719         io->generic.out.create_time   = name->dos.create_time;
720         io->generic.out.access_time   = name->dos.access_time;
721         io->generic.out.write_time    = name->dos.write_time;
722         io->generic.out.change_time   = name->dos.change_time;
723         io->generic.out.attrib        = name->dos.attrib;
724         io->generic.out.alloc_size    = name->dos.alloc_size;
725         io->generic.out.size          = name->st.st_size;
726         io->generic.out.file_type     = FILE_TYPE_DISK;
727         io->generic.out.ipc_state     = 0;
728         io->generic.out.is_directory  = 0;
729
730         /* success - keep the file handle */
731         talloc_steal(pvfs, f);
732
733         notify_trigger(pvfs->notify_context, NOTIFY_ACTION_ADDED, name->full_name);
734
735         return NT_STATUS_OK;
736
737 cleanup_delete:
738         idr_remove(pvfs->files.idtree, fnum);
739         close(fd);
740         unlink(name->full_name);
741         return status;
742 }
743
744
745 /*
746   state of a pending open retry
747 */
748 struct pvfs_open_retry {
749         struct ntvfs_module_context *ntvfs;
750         struct ntvfs_request *req;
751         union smb_open *io;
752         void *wait_handle;
753         DATA_BLOB odb_locking_key;
754 };
755
756 /* destroy a pending open request */
757 static int pvfs_retry_destructor(void *ptr)
758 {
759         struct pvfs_open_retry *r = ptr;
760         struct pvfs_state *pvfs = r->ntvfs->private_data;
761         if (r->odb_locking_key.data) {
762                 struct odb_lock *lck;
763                 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
764                 if (lck != NULL) {
765                         odb_remove_pending(lck, r);
766                 }
767                 talloc_free(lck);
768         }
769         return 0;
770 }
771
772 /*
773   retry an open
774 */
775 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
776 {
777         struct pvfs_open_retry *r = private;
778         struct ntvfs_module_context *ntvfs = r->ntvfs;
779         struct ntvfs_request *req = r->req;
780         union smb_open *io = r->io;
781         NTSTATUS status;
782
783         /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
784            just a bug in their server, but we better do the same */
785         if (reason == PVFS_WAIT_CANCEL) {
786                 return;
787         }
788
789         talloc_free(r->wait_handle);
790
791         if (reason == PVFS_WAIT_TIMEOUT) {
792                 /* if it timed out, then give the failure
793                    immediately */
794                 talloc_free(r);
795                 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
796                 req->async_states->send_fn(req);
797                 return;
798         }
799
800         /* the pending odb entry is already removed. We use a null locking
801            key to indicate this */
802         data_blob_free(&r->odb_locking_key);
803         talloc_free(r);
804
805         /* try the open again, which could trigger another retry setup
806            if it wants to, so we have to unmark the async flag so we
807            will know if it does a second async reply */
808         req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
809
810         status = pvfs_open(ntvfs, req, io);
811         if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
812                 /* the 2nd try also replied async, so we don't send
813                    the reply yet */
814                 return;
815         }
816
817         /* re-mark it async, just in case someone up the chain does
818            paranoid checking */
819         req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
820
821         /* send the reply up the chain */
822         req->async_states->status = status;
823         req->async_states->send_fn(req);
824 }
825
826
827 /*
828   special handling for openx DENY_DOS semantics
829
830   This function attempts a reference open using an existing handle. If its allowed,
831   then it returns NT_STATUS_OK, otherwise it returns any other code and normal
832   open processing continues.
833 */
834 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
835                                    struct ntvfs_request *req, union smb_open *io,
836                                    struct pvfs_file *f, struct odb_lock *lck)
837 {
838         struct pvfs_state *pvfs = ntvfs->private_data;
839         struct pvfs_file *f2;
840         struct pvfs_filename *name;
841
842         /* search for an existing open with the right parameters. Note
843            the magic ntcreatex options flag, which is set in the
844            generic mapping code. This might look ugly, but its
845            actually pretty much now w2k does it internally as well. 
846            
847            If you look at the BASE-DENYDOS test you will see that a
848            DENY_DOS is a very special case, and in the right
849            circumstances you actually get the _same_ handle back
850            twice, rather than a new handle.
851         */
852         for (f2=pvfs->files.list;f2;f2=f2->next) {
853                 if (f2 != f &&
854                     f2->session_info == req->session_info &&
855                     f2->smbpid == req->smbpid &&
856                     (f2->handle->create_options & 
857                      (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
858                       NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
859                     (f2->access_mask & SEC_FILE_WRITE_DATA) &&
860                     strcasecmp_m(f2->handle->name->original_name, 
861                                io->generic.in.fname)==0) {
862                         break;
863                 }
864         }
865
866         if (!f2) {
867                 return NT_STATUS_SHARING_VIOLATION;
868         }
869
870         /* quite an insane set of semantics ... */
871         if (is_exe_filename(io->generic.in.fname) &&
872             (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
873                 return NT_STATUS_SHARING_VIOLATION;
874         }
875
876         /*
877           setup a reference to the existing handle
878          */
879         talloc_free(f->handle);
880         f->handle = talloc_reference(f, f2->handle);
881
882         talloc_free(lck);
883
884         name = f->handle->name;
885
886         io->generic.out.oplock_level  = OPLOCK_NONE;
887         io->generic.out.file.fnum     = f->fnum;
888         io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
889         io->generic.out.create_time   = name->dos.create_time;
890         io->generic.out.access_time   = name->dos.access_time;
891         io->generic.out.write_time    = name->dos.write_time;
892         io->generic.out.change_time   = name->dos.change_time;
893         io->generic.out.attrib        = name->dos.attrib;
894         io->generic.out.alloc_size    = name->dos.alloc_size;
895         io->generic.out.size          = name->st.st_size;
896         io->generic.out.file_type     = FILE_TYPE_DISK;
897         io->generic.out.ipc_state     = 0;
898         io->generic.out.is_directory  = 0;
899
900         talloc_steal(f->pvfs, f);
901
902         return NT_STATUS_OK;
903 }
904
905
906
907 /*
908   setup for a open retry after a sharing violation
909 */
910 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
911                                       struct ntvfs_request *req, 
912                                       union smb_open *io,
913                                       struct pvfs_file *f,
914                                       struct odb_lock *lck)
915 {
916         struct pvfs_state *pvfs = ntvfs->private_data;
917         struct pvfs_open_retry *r;
918         NTSTATUS status;
919         struct timeval end_time;
920
921         if (io->generic.in.create_options & 
922             (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
923                 /* see if we can satisfy the request using the special DENY_DOS
924                    code */
925                 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
926                 if (NT_STATUS_IS_OK(status)) {
927                         return status;
928                 }
929         }
930
931         r = talloc(req, struct pvfs_open_retry);
932         if (r == NULL) {
933                 return NT_STATUS_NO_MEMORY;
934         }
935
936         r->ntvfs = ntvfs;
937         r->req = req;
938         r->io = io;
939         r->odb_locking_key = data_blob_talloc(r, 
940                                               f->handle->odb_locking_key.data, 
941                                               f->handle->odb_locking_key.length);
942
943         end_time = timeval_add(&req->statistics.request_time, 0, pvfs->sharing_violation_delay);
944
945         /* setup a pending lock */
946         status = odb_open_file_pending(lck, r);
947         if (!NT_STATUS_IS_OK(status)) {
948                 return status;
949         }
950
951         talloc_free(lck);
952         talloc_free(f);
953
954         talloc_set_destructor(r, pvfs_retry_destructor);
955
956         r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time, 
957                                            pvfs_open_retry, r);
958         if (r->wait_handle == NULL) {
959                 return NT_STATUS_NO_MEMORY;
960         }
961
962         talloc_steal(pvfs, r);
963
964         return NT_STATUS_OK;
965 }
966
967 /*
968   open a file
969 */
970 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
971                    struct ntvfs_request *req, union smb_open *io)
972 {
973         struct pvfs_state *pvfs = ntvfs->private_data;
974         int flags;
975         struct pvfs_filename *name;
976         struct pvfs_file *f;
977         NTSTATUS status;
978         int fnum, fd;
979         struct odb_lock *lck;
980         uint32_t create_options;
981         uint32_t share_access;
982         uint32_t access_mask;
983         BOOL stream_existed, stream_truncate=False;
984
985         /* use the generic mapping code to avoid implementing all the
986            different open calls. */
987         if (io->generic.level != RAW_OPEN_GENERIC &&
988             io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
989                 return ntvfs_map_open(ntvfs, req, io);
990         }
991
992         /* resolve the cifs name to a posix name */
993         status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 
994                                    PVFS_RESOLVE_STREAMS, &name);
995         if (!NT_STATUS_IS_OK(status)) {
996                 return status;
997         }
998
999         /* directory opens are handled separately */
1000         if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
1001             (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
1002                 return pvfs_open_directory(pvfs, req, name, io);
1003         }
1004
1005         /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
1006            open doesn't match */
1007         io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
1008
1009         create_options = io->generic.in.create_options;
1010         share_access   = io->generic.in.share_access;
1011         access_mask    = io->generic.in.access_mask;
1012
1013         /* certain create options are not allowed */
1014         if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
1015             !(access_mask & SEC_STD_DELETE)) {
1016                 return NT_STATUS_INVALID_PARAMETER;
1017         }
1018
1019         flags = 0;
1020
1021         switch (io->generic.in.open_disposition) {
1022         case NTCREATEX_DISP_SUPERSEDE:
1023         case NTCREATEX_DISP_OVERWRITE_IF:
1024                 if (name->stream_name == NULL) {
1025                         flags = O_TRUNC;
1026                 } else {
1027                         stream_truncate = True;
1028                 }
1029                 break;
1030
1031         case NTCREATEX_DISP_OPEN:
1032                 if (!name->stream_exists) {
1033                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1034                 }
1035                 flags = 0;
1036                 break;
1037
1038         case NTCREATEX_DISP_OVERWRITE:
1039                 if (!name->stream_exists) {
1040                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
1041                 }
1042                 if (name->stream_name == NULL) {
1043                         flags = O_TRUNC;
1044                 } else {
1045                         stream_truncate = True;
1046                 }
1047                 break;
1048
1049         case NTCREATEX_DISP_CREATE:
1050                 if (name->stream_exists) {
1051                         return NT_STATUS_OBJECT_NAME_COLLISION;
1052                 }
1053                 flags = 0;
1054                 break;
1055
1056         case NTCREATEX_DISP_OPEN_IF:
1057                 flags = 0;
1058                 break;
1059
1060         default:
1061                 return NT_STATUS_INVALID_PARAMETER;
1062         }
1063
1064         /* handle creating a new file separately */
1065         if (!name->exists) {
1066                 status = pvfs_create_file(pvfs, req, name, io);
1067                 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
1068                         return status;
1069                 }
1070
1071                 /* we've hit a race - the file was created during this call */
1072                 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
1073                         return status;
1074                 }
1075
1076                 /* try re-resolving the name */
1077                 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
1078                 if (!NT_STATUS_IS_OK(status)) {
1079                         return status;
1080                 }
1081                 /* fall through to a normal open */
1082         }
1083
1084         if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1085             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1086                 return NT_STATUS_CANNOT_DELETE;
1087         }
1088
1089         /* check the security descriptor */
1090         status = pvfs_access_check(pvfs, req, name, &access_mask);
1091         if (!NT_STATUS_IS_OK(status)) {
1092                 return status;
1093         }
1094
1095         f = talloc(req, struct pvfs_file);
1096         if (f == NULL) {
1097                 return NT_STATUS_NO_MEMORY;
1098         }
1099
1100         f->handle = talloc(f, struct pvfs_file_handle);
1101         if (f->handle == NULL) {
1102                 return NT_STATUS_NO_MEMORY;
1103         }
1104
1105         /* allocate a fnum */
1106         fnum = idr_get_new_above(pvfs->files.idtree, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
1107         if (fnum == -1) {
1108                 return NT_STATUS_TOO_MANY_OPENED_FILES;
1109         }
1110
1111         f->fnum          = fnum;
1112         f->session_info  = req->session_info;
1113         f->smbpid        = req->smbpid;
1114         f->pvfs          = pvfs;
1115         f->pending_list  = NULL;
1116         f->lock_count    = 0;
1117         f->share_access  = io->generic.in.share_access;
1118         f->access_mask   = access_mask;
1119         f->impersonation = io->generic.in.impersonation;
1120         f->notify_buffer = NULL;
1121
1122         f->handle->pvfs              = pvfs;
1123         f->handle->fd                = -1;
1124         f->handle->name              = talloc_steal(f->handle, name);
1125         f->handle->create_options    = io->generic.in.create_options;
1126         f->handle->seek_offset       = 0;
1127         f->handle->position          = 0;
1128         f->handle->mode              = 0;
1129         f->handle->have_opendb_entry = False;
1130         f->handle->sticky_write_time = False;
1131
1132         /* form the lock context used for byte range locking and
1133            opendb locking */
1134         status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
1135         if (!NT_STATUS_IS_OK(status)) {
1136                 idr_remove(pvfs->files.idtree, f->fnum);
1137                 return status;
1138         }
1139
1140         status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
1141         if (!NT_STATUS_IS_OK(status)) {
1142                 idr_remove(pvfs->files.idtree, f->fnum);
1143                 return status;
1144         }
1145
1146         /* get a lock on this file before the actual open */
1147         lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1148         if (lck == NULL) {
1149                 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
1150                          name->full_name));
1151                 /* we were supposed to do a blocking lock, so something
1152                    is badly wrong! */
1153                 idr_remove(pvfs->files.idtree, fnum);
1154                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1155         }
1156
1157         DLIST_ADD(pvfs->files.list, f);
1158
1159         /* setup a destructor to avoid file descriptor leaks on
1160            abnormal termination */
1161         talloc_set_destructor(f, pvfs_fnum_destructor);
1162         talloc_set_destructor(f->handle, pvfs_handle_destructor);
1163
1164         /* see if we are allowed to open at the same time as existing opens */
1165         status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
1166                                share_access, access_mask, False, name->full_name);
1167
1168         /* on a sharing violation we need to retry when the file is closed by 
1169            the other user, or after 1 second */
1170         if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
1171             (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
1172                 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
1173         }
1174
1175         if (!NT_STATUS_IS_OK(status)) {
1176                 talloc_free(lck);
1177                 return status;
1178         }
1179
1180         f->handle->have_opendb_entry = True;
1181
1182         if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
1183                 flags |= O_RDWR;
1184         } else {
1185                 flags |= O_RDONLY;
1186         }
1187
1188         /* do the actual open */
1189         fd = open(f->handle->name->full_name, flags);
1190         if (fd == -1) {
1191                 talloc_free(lck);
1192                 return pvfs_map_errno(f->pvfs, errno);
1193         }
1194
1195         f->handle->fd = fd;
1196
1197         stream_existed = name->stream_exists;
1198
1199         /* if this was a stream create then create the stream as well */
1200         if (!name->stream_exists) {
1201                 status = pvfs_stream_create(pvfs, f->handle->name, fd);
1202                 if (!NT_STATUS_IS_OK(status)) {
1203                         talloc_free(lck);
1204                         return status;
1205                 }
1206                 if (stream_truncate) {
1207                         status = pvfs_stream_truncate(pvfs, f->handle->name, fd, 0);
1208                         if (!NT_STATUS_IS_OK(status)) {
1209                                 talloc_free(lck);
1210                                 return status;
1211                         }
1212                 }
1213         }
1214
1215         /* re-resolve the open fd */
1216         status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
1217         if (!NT_STATUS_IS_OK(status)) {
1218                 talloc_free(lck);
1219                 return status;
1220         }
1221
1222         if (f->handle->name->stream_id == 0 &&
1223             (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1224              io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1225                 /* for overwrite we need to replace file permissions */
1226                 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1227                 mode_t mode = pvfs_fileperms(pvfs, attrib);
1228                 if (fchmod(fd, mode) == -1) {
1229                         talloc_free(lck);
1230                         return pvfs_map_errno(pvfs, errno);
1231                 }
1232                 name->dos.attrib = attrib;
1233                 status = pvfs_dosattrib_save(pvfs, name, fd);
1234                 if (!NT_STATUS_IS_OK(status)) {
1235                         talloc_free(lck);
1236                         return status;
1237                 }
1238         }
1239             
1240         talloc_free(lck);
1241
1242         if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
1243                 io->generic.out.oplock_level  = OPLOCK_BATCH;
1244         } else {
1245                 io->generic.out.oplock_level  = OPLOCK_NONE;
1246         }
1247         io->generic.out.file.fnum     = f->fnum;
1248         io->generic.out.create_action = stream_existed?
1249                 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1250         io->generic.out.create_time   = name->dos.create_time;
1251         io->generic.out.access_time   = name->dos.access_time;
1252         io->generic.out.write_time    = name->dos.write_time;
1253         io->generic.out.change_time   = name->dos.change_time;
1254         io->generic.out.attrib        = name->dos.attrib;
1255         io->generic.out.alloc_size    = name->dos.alloc_size;
1256         io->generic.out.size          = name->st.st_size;
1257         io->generic.out.file_type     = FILE_TYPE_DISK;
1258         io->generic.out.ipc_state     = 0;
1259         io->generic.out.is_directory  = 0;
1260
1261         /* success - keep the file handle */
1262         talloc_steal(f->pvfs, f);
1263
1264         return NT_STATUS_OK;
1265 }
1266
1267
1268 /*
1269   close a file
1270 */
1271 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1272                     struct ntvfs_request *req, union smb_close *io)
1273 {
1274         struct pvfs_state *pvfs = ntvfs->private_data;
1275         struct pvfs_file *f;
1276         struct utimbuf unix_times;
1277
1278         if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1279                 return NT_STATUS_DOS(ERRSRV, ERRerror);
1280         }
1281
1282         if (io->generic.level != RAW_CLOSE_CLOSE) {
1283                 return ntvfs_map_close(ntvfs, req, io);
1284         }
1285
1286         f = pvfs_find_fd(pvfs, req, io->close.in.file.fnum);
1287         if (!f) {
1288                 return NT_STATUS_INVALID_HANDLE;
1289         }
1290
1291         if (!null_time(io->close.in.write_time)) {
1292                 unix_times.actime = 0;
1293                 unix_times.modtime = io->close.in.write_time;
1294                 utime(f->handle->name->full_name, &unix_times);
1295         } else if (f->handle->sticky_write_time) {
1296                 unix_times.actime = 0;
1297                 unix_times.modtime = nt_time_to_unix(f->handle->name->dos.write_time);
1298                 utime(f->handle->name->full_name, &unix_times);
1299         }
1300
1301         talloc_free(f);
1302
1303         return NT_STATUS_OK;
1304 }
1305
1306
1307 /*
1308   logoff - close all file descriptors open by a vuid
1309 */
1310 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1311                      struct ntvfs_request *req)
1312 {
1313         struct pvfs_state *pvfs = ntvfs->private_data;
1314         struct pvfs_file *f, *next;
1315
1316         for (f=pvfs->files.list;f;f=next) {
1317                 next = f->next;
1318                 if (f->session_info == req->session_info) {
1319                         talloc_free(f);
1320                 }
1321         }
1322
1323         return NT_STATUS_OK;
1324 }
1325
1326
1327 /*
1328   exit - close files for the current pid
1329 */
1330 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1331                    struct ntvfs_request *req)
1332 {
1333         struct pvfs_state *pvfs = ntvfs->private_data;
1334         struct pvfs_file *f, *next;
1335
1336         for (f=pvfs->files.list;f;f=next) {
1337                 next = f->next;
1338                 if (f->session_info == req->session_info &&
1339                     f->smbpid == req->smbpid) {
1340                         talloc_free(f);
1341                 }
1342         }
1343
1344         return NT_STATUS_OK;
1345 }
1346
1347
1348 /*
1349   change the delete on close flag on an already open file
1350 */
1351 NTSTATUS pvfs_set_delete_on_close(struct pvfs_state *pvfs,
1352                                   struct ntvfs_request *req, 
1353                                   struct pvfs_file *f, BOOL del_on_close)
1354 {
1355         struct odb_lock *lck;
1356         NTSTATUS status;
1357
1358         if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) && del_on_close) {
1359                 return NT_STATUS_CANNOT_DELETE;
1360         }
1361         
1362         if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
1363             !pvfs_directory_empty(pvfs, f->handle->name)) {
1364                 return NT_STATUS_DIRECTORY_NOT_EMPTY;
1365         }
1366
1367         if (del_on_close) {
1368                 f->handle->create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1369         } else {
1370                 f->handle->create_options &= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
1371         }
1372         
1373         lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1374         if (lck == NULL) {
1375                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1376         }
1377
1378         status = odb_set_delete_on_close(lck, del_on_close);
1379
1380         talloc_free(lck);
1381
1382         return status;
1383 }
1384
1385
1386 /*
1387   determine if a file can be deleted, or if it is prevented by an
1388   already open file
1389 */
1390 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs, 
1391                          struct ntvfs_request *req,
1392                          struct pvfs_filename *name,
1393                          struct odb_lock **lckp)
1394 {
1395         NTSTATUS status;
1396         DATA_BLOB key;
1397         struct odb_lock *lck;
1398
1399         status = pvfs_locking_key(name, name, &key);
1400         if (!NT_STATUS_IS_OK(status)) {
1401                 return NT_STATUS_NO_MEMORY;
1402         }
1403
1404         lck = odb_lock(req, pvfs->odb_context, &key);
1405         if (lck == NULL) {
1406                 DEBUG(0,("Unable to lock opendb for can_delete\n"));
1407                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1408         }
1409
1410         status = odb_can_open(lck,
1411                               NTCREATEX_SHARE_ACCESS_READ |
1412                               NTCREATEX_SHARE_ACCESS_WRITE | 
1413                               NTCREATEX_SHARE_ACCESS_DELETE, 
1414                               NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 
1415                               SEC_STD_DELETE);
1416
1417         if (NT_STATUS_IS_OK(status)) {
1418                 status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
1419         }
1420
1421         if (!NT_STATUS_IS_OK(status)) {
1422                 talloc_free(lck);
1423                 *lckp = lck;
1424         } else if (lckp != NULL) {
1425                 *lckp = lck;
1426         }
1427
1428         return status;
1429 }
1430
1431 /*
1432   determine if a file can be renamed, or if it is prevented by an
1433   already open file
1434 */
1435 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs, 
1436                          struct ntvfs_request *req,
1437                          struct pvfs_filename *name,
1438                          struct odb_lock **lckp)
1439 {
1440         NTSTATUS status;
1441         DATA_BLOB key;
1442         struct odb_lock *lck;
1443
1444         status = pvfs_locking_key(name, name, &key);
1445         if (!NT_STATUS_IS_OK(status)) {
1446                 return NT_STATUS_NO_MEMORY;
1447         }
1448
1449         lck = odb_lock(req, pvfs->odb_context, &key);
1450         if (lck == NULL) {
1451                 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1452                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1453         }
1454
1455         status = odb_can_open(lck,
1456                               NTCREATEX_SHARE_ACCESS_READ |
1457                               NTCREATEX_SHARE_ACCESS_WRITE,
1458                               0,
1459                               SEC_STD_DELETE);
1460
1461         if (!NT_STATUS_IS_OK(status)) {
1462                 talloc_free(lck);
1463                 *lckp = lck;
1464         } else if (lckp != NULL) {
1465                 *lckp = lck;
1466         }
1467
1468         return status;
1469 }
1470
1471 /*
1472   determine if file meta data can be accessed, or if it is prevented by an
1473   already open file
1474 */
1475 NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs, 
1476                        struct ntvfs_request *req,
1477                        struct pvfs_filename *name)
1478 {
1479         NTSTATUS status;
1480         DATA_BLOB key;
1481         struct odb_lock *lck;
1482
1483         status = pvfs_locking_key(name, name, &key);
1484         if (!NT_STATUS_IS_OK(status)) {
1485                 return NT_STATUS_NO_MEMORY;
1486         }
1487
1488         lck = odb_lock(req, pvfs->odb_context, &key);
1489         if (lck == NULL) {
1490                 DEBUG(0,("Unable to lock opendb for can_stat\n"));
1491                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1492         }
1493
1494         status = odb_can_open(lck,
1495                               NTCREATEX_SHARE_ACCESS_READ |
1496                               NTCREATEX_SHARE_ACCESS_WRITE,
1497                               0, 0);
1498
1499         return status;
1500 }
1501
1502
1503 /*
1504   determine if delete on close is set on 
1505 */
1506 BOOL pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h, 
1507                               int *open_count, char **path)
1508 {
1509         NTSTATUS status;
1510         BOOL del_on_close;
1511
1512         status = odb_get_delete_on_close(pvfs->odb_context, &h->odb_locking_key, 
1513                                          &del_on_close, open_count, path);
1514         if (!NT_STATUS_IS_OK(status)) {
1515                 DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
1516                 return False;
1517         }
1518
1519         return del_on_close;
1520 }