r3798: added support for alternate data streams in xattrs into pvfs.
[samba.git] / source4 / 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/time.h"
26 #include "system/filesys.h"
27 #include "dlinklist.h"
28 #include "messages.h"
29
30 /*
31   create file handles with convenient numbers for sniffers
32 */
33 #define PVFS_MIN_FILE_FNUM 0x100
34 #define PVFS_MIN_NEW_FNUM  0x200
35 #define PVFS_MIN_DIR_FNUM  0x300
36
37 /*
38   find open file handle given fnum
39 */
40 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
41                                struct smbsrv_request *req, uint16_t fnum)
42 {
43         struct pvfs_file *f;
44
45         f = idr_find(pvfs->idtree_fnum, fnum);
46         if (f == NULL) {
47                 return NULL;
48         }
49
50         if (f->fnum != fnum) {
51                 smb_panic("pvfs_find_fd: idtree_fnum corruption\n");
52         }
53
54         if (req->session != f->session) {
55                 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n", 
56                          fnum));
57                 return NULL;
58         }
59
60         return f;
61 }
62
63
64 /*
65   cleanup a open directory handle
66 */
67 static int pvfs_dir_handle_destructor(void *p)
68 {
69         struct pvfs_file_handle *h = p;
70
71         if (h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
72                 if (rmdir(h->name->full_name) != 0) {
73                         DEBUG(0,("pvfs_close: failed to rmdir '%s' - %s\n", 
74                                  h->name->full_name, strerror(errno)));
75                 }
76         }
77
78         return 0;
79 }
80
81 /*
82   cleanup a open directory fnum
83 */
84 static int pvfs_dir_fnum_destructor(void *p)
85 {
86         struct pvfs_file *f = p;
87         DLIST_REMOVE(f->pvfs->open_files, f);
88         idr_remove(f->pvfs->idtree_fnum, f->fnum);
89         return 0;
90 }
91
92
93 /*
94   open a directory
95 */
96 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, 
97                                     struct smbsrv_request *req, 
98                                     struct pvfs_filename *name, 
99                                     union smb_open *io)
100 {
101         struct pvfs_file *f;
102         int fnum;
103         NTSTATUS status;
104         uint32_t create_action;
105
106         if (name->stream_name) {
107                 return NT_STATUS_NOT_A_DIRECTORY;
108         }
109
110         /* if the client says it must be a directory, and it isn't,
111            then fail */
112         if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
113                 return NT_STATUS_NOT_A_DIRECTORY;
114         }
115
116         switch (io->generic.in.open_disposition) {
117         case NTCREATEX_DISP_OPEN_IF:
118                 break;
119
120         case NTCREATEX_DISP_OPEN:
121                 if (!name->exists) {
122                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
123                 }
124                 break;
125
126         case NTCREATEX_DISP_CREATE:
127                 if (name->exists) {
128                         return NT_STATUS_OBJECT_NAME_COLLISION;
129                 }
130                 break;
131
132         case NTCREATEX_DISP_OVERWRITE_IF:
133         case NTCREATEX_DISP_OVERWRITE:
134         case NTCREATEX_DISP_SUPERSEDE:
135         default:
136                 return NT_STATUS_INVALID_PARAMETER;
137         }
138
139         f = talloc_p(req, struct pvfs_file);
140         if (f == NULL) {
141                 return NT_STATUS_NO_MEMORY;
142         }
143
144         f->handle = talloc_p(f, struct pvfs_file_handle);
145         if (f->handle == NULL) {
146                 return NT_STATUS_NO_MEMORY;
147         }
148
149         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
150         if (fnum == -1) {
151                 return NT_STATUS_TOO_MANY_OPENED_FILES;
152         }
153
154         f->fnum          = fnum;
155         f->session       = req->session;
156         f->smbpid        = req->smbpid;
157         f->pvfs          = pvfs;
158         f->pending_list  = NULL;
159         f->lock_count    = 0;
160         f->share_access  = io->generic.in.share_access;
161         f->impersonation = io->generic.in.impersonation;
162
163         f->handle->pvfs           = pvfs;
164         f->handle->name           = talloc_steal(f->handle, name);
165         f->handle->fd             = -1;
166         f->handle->odb_locking_key    = data_blob(NULL, 0);
167         f->handle->brl_locking_key    = data_blob(NULL, 0);
168         f->handle->create_options = io->generic.in.create_options;
169         f->handle->seek_offset    = 0;
170         f->handle->position       = 0;
171         f->handle->mode           = 0;
172
173         DLIST_ADD(pvfs->open_files, f);
174
175         /* TODO: should we check in the opendb? Do directory opens 
176            follow the share_access rules? */
177
178         /* setup destructors to avoid leaks on abnormal termination */
179         talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
180         talloc_set_destructor(f, pvfs_dir_fnum_destructor);
181
182         if (!name->exists) {
183                 uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
184                 mode_t mode = pvfs_fileperms(pvfs, attrib);
185                 if (mkdir(name->full_name, mode) == -1) {
186                         return pvfs_map_errno(pvfs,errno);
187                 }
188                 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
189                 if (!NT_STATUS_IS_OK(status)) {
190                         return status;
191                 }
192                 create_action = NTCREATEX_ACTION_CREATED;
193         } else {
194                 create_action = NTCREATEX_ACTION_EXISTED;
195         }
196
197         if (!name->exists) {
198                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
199         }
200
201         /* the open succeeded, keep this handle permanently */
202         talloc_steal(pvfs, f);
203
204         io->generic.out.oplock_level  = NO_OPLOCK;
205         io->generic.out.fnum          = f->fnum;
206         io->generic.out.create_action = create_action;
207         io->generic.out.create_time   = name->dos.create_time;
208         io->generic.out.access_time   = name->dos.access_time;
209         io->generic.out.write_time    = name->dos.write_time;
210         io->generic.out.change_time   = name->dos.change_time;
211         io->generic.out.attrib        = name->dos.attrib;
212         io->generic.out.alloc_size    = name->dos.alloc_size;
213         io->generic.out.size          = name->st.st_size;
214         io->generic.out.file_type     = FILE_TYPE_DISK;
215         io->generic.out.ipc_state     = 0;
216         io->generic.out.is_directory  = 1;
217
218         return NT_STATUS_OK;
219 }
220
221 /*
222   destroy a struct pvfs_file_handle
223 */
224 static int pvfs_handle_destructor(void *p)
225 {
226         struct pvfs_file_handle *h = p;
227
228         if (h->fd != -1) {
229                 if (close(h->fd) != 0) {
230                         DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
231                                  h->fd, h->name->full_name, strerror(errno)));
232                 }
233                 h->fd = -1;
234         }
235
236         if (h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
237                 if (unlink(h->name->full_name) != 0) {
238                         DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n", 
239                                  h->name->full_name, strerror(errno)));
240                 }
241         }
242
243         if (h->have_opendb_entry) {
244                 struct odb_lock *lck;
245                 NTSTATUS status;
246
247                 lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
248                 if (lck == NULL) {
249                         DEBUG(0,("Unable to lock opendb for close\n"));
250                         return 0;
251                 }
252
253                 status = odb_close_file(lck, h);
254                 if (!NT_STATUS_IS_OK(status)) {
255                         DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n", 
256                                  h->name->full_name, nt_errstr(status)));
257                 }
258
259                 talloc_free(lck);
260         }
261
262         return 0;
263 }
264
265
266 /*
267   destroy a struct pvfs_file
268 */
269 static int pvfs_fnum_destructor(void *p)
270 {
271         struct pvfs_file *f = p;
272
273         DLIST_REMOVE(f->pvfs->open_files, f);
274         pvfs_lock_close(f->pvfs, f);
275         idr_remove(f->pvfs->idtree_fnum, f->fnum);
276
277         return 0;
278 }
279
280
281 /*
282   form the lock context used for opendb locking. Note that we must
283   zero here to take account of possible padding on some architectures
284 */
285 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name, 
286                                  TALLOC_CTX *mem_ctx, DATA_BLOB *key)
287 {
288         struct {
289                 dev_t device;
290                 ino_t inode;
291         } lock_context;
292         ZERO_STRUCT(lock_context);
293
294         lock_context.device = name->st.st_dev;
295         lock_context.inode = name->st.st_ino;
296
297         *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
298         if (key->data == NULL) {
299                 return NT_STATUS_NO_MEMORY;
300         }
301         
302         return NT_STATUS_OK;
303 }
304
305 /*
306   form the lock context used for byte range locking. This is separate
307   from the locking key used for opendb locking as it needs to take
308   account of file streams (each stream is a separate byte range
309   locking space)
310 */
311 static NTSTATUS pvfs_brl_locking_key(struct pvfs_filename *name, 
312                                      TALLOC_CTX *mem_ctx, DATA_BLOB *key)
313 {
314         DATA_BLOB odb_key;
315         NTSTATUS status;
316         status = pvfs_locking_key(name, mem_ctx, &odb_key);
317         if (!NT_STATUS_IS_OK(status)) {
318                 return status;
319         }
320         if (name->stream_name == NULL) {
321                 *key = odb_key;
322                 return NT_STATUS_OK;
323         }
324         *key = data_blob_talloc(mem_ctx, NULL, 
325                                 odb_key.length + strlen(name->stream_name) + 1);
326         if (key->data == NULL) {
327                 return NT_STATUS_NO_MEMORY;
328         }
329         memcpy(key->data, odb_key.data, odb_key.length);
330         memcpy(key->data + odb_key.length, 
331                name->stream_name, strlen(name->stream_name)+1);
332         data_blob_free(&odb_key);
333         return NT_STATUS_OK;
334 }
335
336
337 /*
338   create a new file
339 */
340 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, 
341                                  struct smbsrv_request *req, 
342                                  struct pvfs_filename *name, 
343                                  union smb_open *io)
344 {
345         struct pvfs_file *f;
346         NTSTATUS status;
347         int flags, fnum, fd;
348         struct odb_lock *lck;
349         uint32_t create_options = io->generic.in.create_options;
350         uint32_t share_access = io->generic.in.share_access;
351         uint32_t access_mask = io->generic.in.access_mask;
352         mode_t mode;
353         uint32_t attrib;
354
355         if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
356             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
357                 return NT_STATUS_CANNOT_DELETE;
358         }
359         
360         if (access_mask & SEC_RIGHT_MAXIMUM_ALLOWED) {
361                 access_mask = GENERIC_RIGHTS_FILE_READ | GENERIC_RIGHTS_FILE_WRITE;
362         }
363
364         if (access_mask & SA_RIGHT_FILE_WRITE_APPEND) {
365                 flags = O_RDWR;
366         } else {
367                 flags = O_RDONLY;
368         }
369
370         f = talloc_p(req, struct pvfs_file);
371         if (f == NULL) {
372                 return NT_STATUS_NO_MEMORY;
373         }
374
375         f->handle = talloc_p(f, struct pvfs_file_handle);
376         if (f->handle == NULL) {
377                 return NT_STATUS_NO_MEMORY;
378         }
379
380         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
381         if (fnum == -1) {
382                 return NT_STATUS_TOO_MANY_OPENED_FILES;
383         }
384
385         attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
386         mode = pvfs_fileperms(pvfs, attrib);
387
388         /* create the file */
389         fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
390         if (fd == -1) {
391                 idr_remove(pvfs->idtree_fnum, fnum);
392                 return pvfs_map_errno(pvfs, errno);
393         }
394
395         /* if this was a stream create then create the stream as well */
396         if (name->stream_name) {
397                 status = pvfs_stream_create(pvfs, name, fd);
398                 if (!NT_STATUS_IS_OK(status)) {
399                         idr_remove(pvfs->idtree_fnum, fnum);
400                         close(fd);
401                         return status;
402                 }
403         }
404
405         /* re-resolve the open fd */
406         status = pvfs_resolve_name_fd(pvfs, fd, name);
407         if (!NT_STATUS_IS_OK(status)) {
408                 idr_remove(pvfs->idtree_fnum, fnum);
409                 close(fd);
410                 return status;
411         }
412
413         name->dos.attrib = attrib;
414         status = pvfs_dosattrib_save(pvfs, name, fd);
415         if (!NT_STATUS_IS_OK(status)) {
416                 idr_remove(pvfs->idtree_fnum, fnum);
417                 close(fd);
418                 return status;
419         }
420
421         /* form the lock context used for byte range locking and
422            opendb locking */
423         status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
424         if (!NT_STATUS_IS_OK(status)) {
425                 idr_remove(pvfs->idtree_fnum, fnum);
426                 close(fd);
427                 return status;
428         }
429
430         status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
431         if (!NT_STATUS_IS_OK(status)) {
432                 idr_remove(pvfs->idtree_fnum, fnum);
433                 close(fd);
434                 return status;
435         }
436
437         /* grab a lock on the open file record */
438         lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
439         if (lck == NULL) {
440                 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
441                          name->full_name));
442                 /* we were supposed to do a blocking lock, so something
443                    is badly wrong! */
444                 idr_remove(pvfs->idtree_fnum, fnum);
445                 close(fd);
446                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
447         }
448
449         status = odb_open_file(lck, f->handle, name->stream_id,
450                                share_access, create_options, access_mask);
451         talloc_free(lck);
452         if (!NT_STATUS_IS_OK(status)) {
453                 /* bad news, we must have hit a race */
454                 idr_remove(pvfs->idtree_fnum, fnum);
455                 close(fd);
456                 return status;
457         }
458
459         f->fnum              = fnum;
460         f->session           = req->session;
461         f->smbpid            = req->smbpid;
462         f->pvfs              = pvfs;
463         f->pending_list      = NULL;
464         f->lock_count        = 0;
465         f->share_access      = io->generic.in.share_access;
466         f->access_mask       = access_mask;
467         f->impersonation     = io->generic.in.impersonation;
468
469         f->handle->pvfs              = pvfs;
470         f->handle->name              = talloc_steal(f->handle, name);
471         f->handle->fd                = fd;
472         f->handle->create_options    = io->generic.in.create_options;
473         f->handle->seek_offset       = 0;
474         f->handle->position          = 0;
475         f->handle->mode              = 0;
476         f->handle->have_opendb_entry = True;
477
478         DLIST_ADD(pvfs->open_files, f);
479
480         /* setup a destructor to avoid file descriptor leaks on
481            abnormal termination */
482         talloc_set_destructor(f, pvfs_fnum_destructor);
483         talloc_set_destructor(f->handle, pvfs_handle_destructor);
484
485         io->generic.out.oplock_level  = NO_OPLOCK;
486         io->generic.out.fnum          = f->fnum;
487         io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
488         io->generic.out.create_time   = name->dos.create_time;
489         io->generic.out.access_time   = name->dos.access_time;
490         io->generic.out.write_time    = name->dos.write_time;
491         io->generic.out.change_time   = name->dos.change_time;
492         io->generic.out.attrib        = name->dos.attrib;
493         io->generic.out.alloc_size    = name->dos.alloc_size;
494         io->generic.out.size          = name->st.st_size;
495         io->generic.out.file_type     = FILE_TYPE_DISK;
496         io->generic.out.ipc_state     = 0;
497         io->generic.out.is_directory  = 0;
498
499         /* success - keep the file handle */
500         talloc_steal(pvfs, f);
501
502         return NT_STATUS_OK;
503 }
504
505
506 /*
507   state of a pending open retry
508 */
509 struct pvfs_open_retry {
510         struct ntvfs_module_context *ntvfs;
511         struct smbsrv_request *req;
512         union smb_open *io;
513         void *wait_handle;
514         DATA_BLOB odb_locking_key;
515 };
516
517 /* destroy a pending open request */
518 static int pvfs_retry_destructor(void *ptr)
519 {
520         struct pvfs_open_retry *r = ptr;
521         struct pvfs_state *pvfs = r->ntvfs->private_data;
522         if (r->odb_locking_key.data) {
523                 struct odb_lock *lck;
524                 lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
525                 if (lck != NULL) {
526                         odb_remove_pending(lck, r);
527                 }
528                 talloc_free(lck);
529         }
530         return 0;
531 }
532
533 /*
534   retry an open
535 */
536 static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
537 {
538         struct pvfs_open_retry *r = private;
539         struct ntvfs_module_context *ntvfs = r->ntvfs;
540         struct smbsrv_request *req = r->req;
541         union smb_open *io = r->io;
542         NTSTATUS status;
543
544         /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
545            just a bug in their server, but we better do the same */
546         if (reason == PVFS_WAIT_CANCEL) {
547                 return;
548         }
549
550         talloc_free(r->wait_handle);
551
552         if (reason == PVFS_WAIT_TIMEOUT) {
553                 /* if it timed out, then give the failure
554                    immediately */
555                 talloc_free(r);
556                 req->async_states->status = NT_STATUS_SHARING_VIOLATION;
557                 req->async_states->send_fn(req);
558                 return;
559         }
560
561         /* the pending odb entry is already removed. We use a null locking
562            key to indicate this */
563         data_blob_free(&r->odb_locking_key);
564         talloc_free(r);
565
566         /* try the open again, which could trigger another retry setup
567            if it wants to, so we have to unmark the async flag so we
568            will know if it does a second async reply */
569         req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
570
571         status = pvfs_open(ntvfs, req, io);
572         if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
573                 /* the 2nd try also replied async, so we don't send
574                    the reply yet */
575                 return;
576         }
577
578         /* re-mark it async, just in case someone up the chain does
579            paranoid checking */
580         req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
581
582         /* send the reply up the chain */
583         req->async_states->status = status;
584         req->async_states->send_fn(req);
585 }
586
587
588 /*
589   special handling for openx DENY_DOS semantics
590
591   This function attempts a reference open using an existing handle. If its allowed,
592   then it returns NT_STATUS_OK, otherwise it returns any other code and normal
593   open processing continues.
594 */
595 static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
596                                    struct smbsrv_request *req, union smb_open *io,
597                                    struct pvfs_file *f, struct odb_lock *lck)
598 {
599         struct pvfs_state *pvfs = ntvfs->private_data;
600         struct pvfs_file *f2;
601         struct pvfs_filename *name;
602
603         /* search for an existing open with the right parameters. Note
604            the magic ntcreatex options flag, which is set in the
605            generic mapping code. This might look ugly, but its
606            actually pretty much now w2k does it internally as well. 
607            
608            If you look at the BASE-DENYDOS test you will see that a
609            DENY_DOS is a very special case, and in the right
610            circumstances you actually get the _same_ handle back
611            twice, rather than a new handle.
612         */
613         for (f2=pvfs->open_files;f2;f2=f2->next) {
614                 if (f2 != f &&
615                     f2->session == req->session &&
616                     f2->smbpid == req->smbpid &&
617                     (f2->handle->create_options & 
618                      (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS |
619                       NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) &&
620                     (f2->access_mask & SA_RIGHT_FILE_WRITE_DATA) &&
621                     StrCaseCmp(f2->handle->name->original_name, 
622                                io->generic.in.fname)==0) {
623                         break;
624                 }
625         }
626
627         if (!f2) {
628                 return NT_STATUS_SHARING_VIOLATION;
629         }
630
631         /* quite an insane set of semantics ... */
632         if (is_exe_filename(io->generic.in.fname) &&
633             (f2->handle->create_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) {
634                 return NT_STATUS_SHARING_VIOLATION;
635         }
636
637         /*
638           setup a reference to the existing handle
639          */
640         talloc_free(f->handle);
641         f->handle = talloc_reference(f, f2->handle);
642
643         talloc_free(lck);
644
645         name = f->handle->name;
646
647         io->generic.out.oplock_level  = NO_OPLOCK;
648         io->generic.out.fnum          = f->fnum;
649         io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
650         io->generic.out.create_time   = name->dos.create_time;
651         io->generic.out.access_time   = name->dos.access_time;
652         io->generic.out.write_time    = name->dos.write_time;
653         io->generic.out.change_time   = name->dos.change_time;
654         io->generic.out.attrib        = name->dos.attrib;
655         io->generic.out.alloc_size    = name->dos.alloc_size;
656         io->generic.out.size          = name->st.st_size;
657         io->generic.out.file_type     = FILE_TYPE_DISK;
658         io->generic.out.ipc_state     = 0;
659         io->generic.out.is_directory  = 0;
660
661         talloc_steal(f->pvfs, f);
662
663         return NT_STATUS_OK;
664 }
665
666
667
668 /*
669   setup for a open retry after a sharing violation
670 */
671 static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
672                                       struct smbsrv_request *req, 
673                                       union smb_open *io,
674                                       struct pvfs_file *f,
675                                       struct odb_lock *lck)
676 {
677         struct pvfs_state *pvfs = ntvfs->private_data;
678         struct pvfs_open_retry *r;
679         NTSTATUS status;
680         struct timeval end_time;
681
682         if (io->generic.in.create_options & 
683             (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
684                 /* see if we can satisfy the request using the special DENY_DOS
685                    code */
686                 status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
687                 if (NT_STATUS_IS_OK(status)) {
688                         return status;
689                 }
690         }
691
692         r = talloc_p(req, struct pvfs_open_retry);
693         if (r == NULL) {
694                 return NT_STATUS_NO_MEMORY;
695         }
696
697         r->ntvfs = ntvfs;
698         r->req = req;
699         r->io = io;
700         r->odb_locking_key = data_blob_talloc(r, 
701                                               f->handle->odb_locking_key.data, 
702                                               f->handle->odb_locking_key.length);
703
704         end_time = timeval_add(&req->request_time, 0, pvfs->sharing_violation_delay);
705
706         /* setup a pending lock */
707         status = odb_open_file_pending(lck, r);
708         if (!NT_STATUS_IS_OK(status)) {
709                 return status;
710         }
711
712         talloc_free(lck);
713         talloc_free(f);
714
715         talloc_set_destructor(r, pvfs_retry_destructor);
716
717         r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time, 
718                                            pvfs_open_retry, r);
719         if (r->wait_handle == NULL) {
720                 return NT_STATUS_NO_MEMORY;
721         }
722
723         talloc_steal(pvfs, r);
724
725         return NT_STATUS_OK;
726 }
727
728 /*
729   special handling for t2open
730 */
731 static NTSTATUS pvfs_open_t2open(struct ntvfs_module_context *ntvfs,
732                                  struct smbsrv_request *req, union smb_open *io)
733 {
734         struct pvfs_state *pvfs = ntvfs->private_data;
735         struct pvfs_filename *name;
736         NTSTATUS status;
737
738         status = pvfs_resolve_name(pvfs, req, io->t2open.in.fname, 0, &name);
739         if (!NT_STATUS_IS_OK(status)) {
740                 return status;
741         }
742
743         if (io->t2open.in.open_func & OPENX_OPEN_FUNC_CREATE) {
744                 if (!name->stream_exists) return NT_STATUS_ACCESS_DENIED;
745         }
746         if (io->t2open.in.open_func & OPENX_OPEN_FUNC_TRUNC) {
747                 if (name->stream_exists) return NT_STATUS_ACCESS_DENIED;
748                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
749         }
750         if ((io->t2open.in.open_func & 0xF) == OPENX_OPEN_FUNC_FAIL) {
751                 if (!name->stream_exists) return NT_STATUS_ACCESS_DENIED;
752                 return NT_STATUS_OBJECT_NAME_COLLISION;
753         }
754
755         talloc_free(name);
756
757         return ntvfs_map_open(req, io, ntvfs);
758 }
759
760 /*
761   open a file
762 */
763 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
764                    struct smbsrv_request *req, union smb_open *io)
765 {
766         struct pvfs_state *pvfs = ntvfs->private_data;
767         int flags;
768         struct pvfs_filename *name;
769         struct pvfs_file *f;
770         NTSTATUS status;
771         int fnum, fd;
772         struct odb_lock *lck;
773         uint32_t create_options;
774         uint32_t share_access;
775         uint32_t access_mask;
776         BOOL stream_existed;
777
778         if (io->generic.level == RAW_OPEN_T2OPEN) {
779                 return pvfs_open_t2open(ntvfs, req, io);
780         }
781
782         /* use the generic mapping code to avoid implementing all the
783            different open calls. */
784         if (io->generic.level != RAW_OPEN_GENERIC) {
785                 return ntvfs_map_open(req, io, ntvfs);
786         }
787
788         /* resolve the cifs name to a posix name */
789         status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 
790                                    PVFS_RESOLVE_STREAMS, &name);
791         if (!NT_STATUS_IS_OK(status)) {
792                 return status;
793         }
794
795         /* directory opens are handled separately */
796         if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
797             (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
798                 return pvfs_open_directory(pvfs, req, name, io);
799         }
800
801         create_options = io->generic.in.create_options;
802         share_access   = io->generic.in.share_access;
803         access_mask    = io->generic.in.access_mask;
804
805         if (access_mask & SEC_RIGHT_MAXIMUM_ALLOWED) {
806                 if (name->exists && (name->dos.attrib & FILE_ATTRIBUTE_READONLY)) {
807                         access_mask = GENERIC_RIGHTS_FILE_READ;
808                 } else {
809                         access_mask = GENERIC_RIGHTS_FILE_READ | GENERIC_RIGHTS_FILE_WRITE;
810                 }
811         }
812
813         /* certain create options are not allowed */
814         if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
815             !(access_mask & STD_RIGHT_DELETE_ACCESS)) {
816                 return NT_STATUS_INVALID_PARAMETER;
817         }
818
819         switch (io->generic.in.open_disposition) {
820         case NTCREATEX_DISP_SUPERSEDE:
821                 flags = O_TRUNC;
822                 break;
823
824         case NTCREATEX_DISP_OVERWRITE_IF:
825                 flags = O_TRUNC;
826                 break;
827
828         case NTCREATEX_DISP_OPEN:
829                 if (!name->stream_exists) {
830                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
831                 }
832                 flags = 0;
833                 break;
834
835         case NTCREATEX_DISP_OVERWRITE:
836                 if (!name->stream_exists) {
837                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
838                 }
839                 flags = O_TRUNC;
840                 break;
841
842         case NTCREATEX_DISP_CREATE:
843                 if (name->stream_exists) {
844                         return NT_STATUS_OBJECT_NAME_COLLISION;
845                 }
846                 flags = 0;
847                 break;
848
849         case NTCREATEX_DISP_OPEN_IF:
850                 flags = 0;
851                 break;
852
853         default:
854                 return NT_STATUS_INVALID_PARAMETER;
855         }
856
857         if (access_mask & SA_RIGHT_FILE_WRITE_APPEND) {
858                 flags |= O_RDWR;
859         } else {
860                 flags |= O_RDONLY;
861         }
862
863         if (io->generic.in.file_attr & FILE_ATTRIBUTE_DIRECTORY) {
864                 return NT_STATUS_INVALID_PARAMETER;
865         }
866
867         /* handle creating a new file separately */
868         if (!name->exists) {
869                 status = pvfs_create_file(pvfs, req, name, io);
870                 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
871                         return status;
872                 }
873
874                 /* we've hit a race - the file was created during this call */
875                 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
876                         return status;
877                 }
878
879                 /* try re-resolving the name */
880                 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
881                 if (!NT_STATUS_IS_OK(status)) {
882                         return status;
883                 }
884                 /* fall through to a normal open */
885         }
886
887         if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
888             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
889                 return NT_STATUS_CANNOT_DELETE;
890         }
891
892         f = talloc_p(req, struct pvfs_file);
893         if (f == NULL) {
894                 return NT_STATUS_NO_MEMORY;
895         }
896
897         f->handle = talloc_p(f, struct pvfs_file_handle);
898         if (f->handle == NULL) {
899                 return NT_STATUS_NO_MEMORY;
900         }
901
902         /* allocate a fnum */
903         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
904         if (fnum == -1) {
905                 return NT_STATUS_TOO_MANY_OPENED_FILES;
906         }
907
908         f->fnum          = fnum;
909         f->session       = req->session;
910         f->smbpid        = req->smbpid;
911         f->pvfs          = pvfs;
912         f->pending_list  = NULL;
913         f->lock_count    = 0;
914         f->share_access  = io->generic.in.share_access;
915         f->access_mask   = access_mask;
916         f->impersonation = io->generic.in.impersonation;
917
918         f->handle->pvfs              = pvfs;
919         f->handle->fd                = -1;
920         f->handle->name              = talloc_steal(f->handle, name);
921         f->handle->create_options    = io->generic.in.create_options;
922         f->handle->seek_offset       = 0;
923         f->handle->position          = 0;
924         f->handle->have_opendb_entry = False;
925
926         /* form the lock context used for byte range locking and
927            opendb locking */
928         status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
929         if (!NT_STATUS_IS_OK(status)) {
930                 idr_remove(pvfs->idtree_fnum, f->fnum);
931                 return status;
932         }
933
934         status = pvfs_brl_locking_key(name, f->handle, &f->handle->brl_locking_key);
935         if (!NT_STATUS_IS_OK(status)) {
936                 idr_remove(pvfs->idtree_fnum, f->fnum);
937                 return status;
938         }
939
940         /* get a lock on this file before the actual open */
941         lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
942         if (lck == NULL) {
943                 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
944                          name->full_name));
945                 /* we were supposed to do a blocking lock, so something
946                    is badly wrong! */
947                 idr_remove(pvfs->idtree_fnum, fnum);
948                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
949         }
950
951         DLIST_ADD(pvfs->open_files, f);
952
953         /* setup a destructor to avoid file descriptor leaks on
954            abnormal termination */
955         talloc_set_destructor(f, pvfs_fnum_destructor);
956         talloc_set_destructor(f->handle, pvfs_handle_destructor);
957
958
959         /* see if we are allowed to open at the same time as existing opens */
960         status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
961                                share_access, create_options, access_mask);
962
963         /* on a sharing violation we need to retry when the file is closed by 
964            the other user, or after 1 second */
965         if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
966             (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
967                 return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
968         }
969
970         if (!NT_STATUS_IS_OK(status)) {
971                 talloc_free(lck);
972                 return status;
973         }
974
975         f->handle->have_opendb_entry = True;
976
977         /* do the actual open */
978         fd = open(f->handle->name->full_name, flags);
979         if (fd == -1) {
980                 talloc_free(lck);
981                 return pvfs_map_errno(f->pvfs, errno);
982         }
983
984         f->handle->fd = fd;
985
986         stream_existed = name->stream_exists;
987
988         /* if this was a stream create then create the stream as well */
989         if (!name->stream_exists) {
990                 status = pvfs_stream_create(pvfs, f->handle->name, fd);
991                 if (!NT_STATUS_IS_OK(status)) {
992                         talloc_free(lck);
993                         return status;
994                 }
995         }
996
997         /* re-resolve the open fd */
998         status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name);
999         if (!NT_STATUS_IS_OK(status)) {
1000                 talloc_free(lck);
1001                 return status;
1002         }
1003
1004         if (f->handle->name->stream_id == 0 &&
1005             (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
1006              io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
1007                 /* for overwrite we need to replace file permissions */
1008                 uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
1009                 mode_t mode = pvfs_fileperms(pvfs, attrib);
1010                 if (fchmod(fd, mode) == -1) {
1011                         talloc_free(lck);
1012                         return pvfs_map_errno(pvfs, errno);
1013                 }
1014                 name->dos.attrib = attrib;
1015                 status = pvfs_dosattrib_save(pvfs, name, fd);
1016                 if (!NT_STATUS_IS_OK(status)) {
1017                         talloc_free(lck);
1018                         return status;
1019                 }
1020         }
1021             
1022         talloc_free(lck);
1023
1024         io->generic.out.oplock_level  = NO_OPLOCK;
1025         io->generic.out.fnum          = f->fnum;
1026         io->generic.out.create_action = stream_existed?
1027                 NTCREATEX_ACTION_EXISTED:NTCREATEX_ACTION_CREATED;
1028         io->generic.out.create_time   = name->dos.create_time;
1029         io->generic.out.access_time   = name->dos.access_time;
1030         io->generic.out.write_time    = name->dos.write_time;
1031         io->generic.out.change_time   = name->dos.change_time;
1032         io->generic.out.attrib        = name->dos.attrib;
1033         io->generic.out.alloc_size    = name->dos.alloc_size;
1034         io->generic.out.size          = name->st.st_size;
1035         io->generic.out.file_type     = FILE_TYPE_DISK;
1036         io->generic.out.ipc_state     = 0;
1037         io->generic.out.is_directory  = 0;
1038
1039         /* success - keep the file handle */
1040         talloc_steal(f->pvfs, f);
1041
1042         return NT_STATUS_OK;
1043 }
1044
1045
1046 /*
1047   close a file
1048 */
1049 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
1050                     struct smbsrv_request *req, union smb_close *io)
1051 {
1052         struct pvfs_state *pvfs = ntvfs->private_data;
1053         struct pvfs_file *f;
1054         struct utimbuf unix_times;
1055
1056         if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
1057                 return NT_STATUS_UNSUCCESSFUL;
1058         }
1059
1060         if (io->generic.level != RAW_CLOSE_CLOSE) {
1061                 return ntvfs_map_close(req, io, ntvfs);
1062         }
1063
1064         f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
1065         if (!f) {
1066                 return NT_STATUS_INVALID_HANDLE;
1067         }
1068
1069         if (!null_time(io->close.in.write_time)) {
1070                 unix_times.actime = 0;
1071                 unix_times.modtime = io->close.in.write_time;
1072                 utime(f->handle->name->full_name, &unix_times);
1073         }
1074         
1075         talloc_free(f);
1076
1077         return NT_STATUS_OK;
1078 }
1079
1080
1081 /*
1082   logoff - close all file descriptors open by a vuid
1083 */
1084 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
1085                      struct smbsrv_request *req)
1086 {
1087         struct pvfs_state *pvfs = ntvfs->private_data;
1088         struct pvfs_file *f, *next;
1089
1090         for (f=pvfs->open_files;f;f=next) {
1091                 next = f->next;
1092                 if (f->session == req->session) {
1093                         talloc_free(f);
1094                 }
1095         }
1096
1097         return NT_STATUS_OK;
1098 }
1099
1100
1101 /*
1102   exit - close files for the current pid
1103 */
1104 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
1105                    struct smbsrv_request *req)
1106 {
1107         struct pvfs_state *pvfs = ntvfs->private_data;
1108         struct pvfs_file *f, *next;
1109
1110         for (f=pvfs->open_files;f;f=next) {
1111                 next = f->next;
1112                 if (f->smbpid == req->smbpid) {
1113                         talloc_free(f);
1114                 }
1115         }
1116
1117         return NT_STATUS_OK;
1118 }
1119
1120
1121 /*
1122   change the create options on an already open file
1123 */
1124 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
1125                                     struct smbsrv_request *req, 
1126                                     struct pvfs_file *f, uint32_t create_options)
1127 {
1128         struct odb_lock *lck;
1129         NTSTATUS status;
1130
1131         if (f->handle->create_options == create_options) {
1132                 return NT_STATUS_OK;
1133         }
1134
1135         if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
1136             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
1137                 return NT_STATUS_CANNOT_DELETE;
1138         }
1139
1140         lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
1141         if (lck == NULL) {
1142                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
1143         }
1144
1145         status = odb_set_create_options(lck, f->handle, create_options);
1146         if (NT_STATUS_IS_OK(status)) {
1147                 f->handle->create_options = create_options;
1148         }
1149
1150         return status;
1151 }
1152
1153
1154 /*
1155   determine if a file can be deleted, or if it is prevented by an
1156   already open file
1157 */
1158 NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs, struct pvfs_filename *name)
1159 {
1160         NTSTATUS status;
1161         DATA_BLOB key;
1162
1163         status = pvfs_locking_key(name, name, &key);
1164         if (!NT_STATUS_IS_OK(status)) {
1165                 return NT_STATUS_NO_MEMORY;
1166         }
1167
1168         status = odb_can_open(pvfs->odb_context, &key, 
1169                               NTCREATEX_SHARE_ACCESS_READ |
1170                               NTCREATEX_SHARE_ACCESS_WRITE | 
1171                               NTCREATEX_SHARE_ACCESS_DELETE, 
1172                               NTCREATEX_OPTIONS_DELETE_ON_CLOSE, 
1173                               STD_RIGHT_DELETE_ACCESS);
1174
1175         return status;
1176 }
1177
1178 /*
1179   determine if a file can be renamed, or if it is prevented by an
1180   already open file
1181 */
1182 NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs, struct pvfs_filename *name)
1183 {
1184         NTSTATUS status;
1185         DATA_BLOB key;
1186
1187         status = pvfs_locking_key(name, name, &key);
1188         if (!NT_STATUS_IS_OK(status)) {
1189                 return NT_STATUS_NO_MEMORY;
1190         }
1191
1192         status = odb_can_open(pvfs->odb_context, &key, 
1193                               NTCREATEX_SHARE_ACCESS_READ |
1194                               NTCREATEX_SHARE_ACCESS_WRITE,
1195                               0,
1196                               STD_RIGHT_DELETE_ACCESS);
1197
1198         return status;
1199 }