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