r3159: use easy to recognise file handle numbers for new file, old file and directory
[bbaumbach/samba-autobuild/.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 "include/includes.h"
24 #include "vfs_posix.h"
25
26 /*
27   create file handles with convenient numbers for sniffers
28 */
29 #define PVFS_MIN_FILE_FNUM 0x100
30 #define PVFS_MIN_NEW_FNUM 0x200
31 #define PVFS_MIN_DIR_FNUM 0x1000
32
33 /*
34   find open file handle given fnum
35 */
36 struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
37                                struct smbsrv_request *req, uint16_t fnum)
38 {
39         struct pvfs_file *f;
40
41         f = idr_find(pvfs->idtree_fnum, fnum);
42         if (f == NULL) {
43                 return NULL;
44         }
45
46         if (req->session != f->session) {
47                 DEBUG(2,("pvfs_find_fd: attempt to use wrong session for fnum %d\n", 
48                          fnum));
49                 return NULL;
50         }
51
52         return f;
53 }
54
55
56 /*
57   cleanup a open directory handle
58 */
59 static int pvfs_dir_fd_destructor(void *p)
60 {
61         struct pvfs_file *f = p;
62         DLIST_REMOVE(f->pvfs->open_files, f);
63         idr_remove(f->pvfs->idtree_fnum, f->fnum);
64
65         if (f->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
66                 if (rmdir(f->name->full_name) != 0) {
67                         DEBUG(0,("pvfs_close: failed to rmdir '%s'\n", 
68                                  f->name->full_name));
69                 }
70         }
71
72         return 0;
73 }
74
75
76 /*
77   open a directory
78 */
79 static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs, 
80                                     struct smbsrv_request *req, 
81                                     struct pvfs_filename *name, 
82                                     union smb_open *io)
83 {
84         struct pvfs_file *f;
85         int fnum;
86         NTSTATUS status;
87         uint32_t create_action;
88
89         /* if the client says it must be a directory, and it isn't,
90            then fail */
91         if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
92                 return NT_STATUS_NOT_A_DIRECTORY;
93         }
94
95         switch (io->generic.in.open_disposition) {
96         case NTCREATEX_DISP_OPEN_IF:
97                 break;
98
99         case NTCREATEX_DISP_OPEN:
100                 if (!name->exists) {
101                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
102                 }
103                 break;
104
105         case NTCREATEX_DISP_CREATE:
106                 if (name->exists) {
107                         return NT_STATUS_OBJECT_NAME_COLLISION;
108                 }
109                 break;
110
111         case NTCREATEX_DISP_OVERWRITE_IF:
112         case NTCREATEX_DISP_OVERWRITE:
113         case NTCREATEX_DISP_SUPERSEDE:
114         default:
115                 return NT_STATUS_INVALID_PARAMETER;
116         }
117
118         f = talloc_p(req, struct pvfs_file);
119         if (f == NULL) {
120                 return NT_STATUS_NO_MEMORY;
121         }
122
123         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_DIR_FNUM, UINT16_MAX);
124         if (fnum == -1) {
125                 talloc_free(f);
126                 return NT_STATUS_TOO_MANY_OPENED_FILES;
127         }
128
129         f->fnum = fnum;
130         f->fd = -1;
131         f->name = talloc_steal(f, name);
132         f->session = req->session;
133         f->smbpid = req->smbpid;
134         f->pvfs = pvfs;
135         f->pending_list = NULL;
136         f->lock_count = 0;
137         f->locking_key = data_blob(NULL, 0);
138         f->create_options = io->generic.in.create_options;
139         f->share_access = io->generic.in.share_access;
140
141         DLIST_ADD(pvfs->open_files, f);
142
143         /* TODO: should we check in the opendb? Do directory opens 
144            follow the share_access rules? */
145
146
147         /* setup a destructor to avoid leaks on abnormal termination */
148         talloc_set_destructor(f, pvfs_dir_fd_destructor);
149
150         if (!name->exists) {
151                 if (mkdir(name->full_name, 0755) == -1) {
152                         return pvfs_map_errno(pvfs,errno);
153                 }
154                 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
155                                            PVFS_RESOLVE_NO_WILDCARD, &name);
156                 if (!NT_STATUS_IS_OK(status)) {
157                         return status;
158                 }
159                 create_action = NTCREATEX_ACTION_CREATED;
160         } else {
161                 create_action = NTCREATEX_ACTION_EXISTED;
162         }
163
164         if (!name->exists) {
165                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
166         }
167
168         /* the open succeeded, keep this handle permanently */
169         talloc_steal(pvfs, f);
170
171         io->generic.out.oplock_level  = NO_OPLOCK;
172         io->generic.out.fnum          = f->fnum;
173         io->generic.out.create_action = create_action;
174         io->generic.out.create_time   = name->dos.create_time;
175         io->generic.out.access_time   = name->dos.access_time;
176         io->generic.out.write_time    = name->dos.write_time;
177         io->generic.out.change_time   = name->dos.change_time;
178         io->generic.out.attrib        = name->dos.attrib;
179         io->generic.out.alloc_size    = 0;
180         io->generic.out.size          = 0;
181         io->generic.out.file_type     = FILE_TYPE_DISK;
182         io->generic.out.ipc_state     = 0;
183         io->generic.out.is_directory  = 1;
184
185         return NT_STATUS_OK;
186 }
187
188
189 /*
190   by using a destructor we make sure that abnormal cleanup will not 
191   leak file descriptors (assuming at least the top level pointer is freed, which
192   will cascade down to here)
193 */
194 static int pvfs_fd_destructor(void *p)
195 {
196         struct pvfs_file *f = p;
197         struct odb_lock *lck;
198         NTSTATUS status;
199
200         DLIST_REMOVE(f->pvfs->open_files, f);
201
202         pvfs_lock_close(f->pvfs, f);
203
204         if (f->fd != -1) {
205                 close(f->fd);
206                 f->fd = -1;
207         }
208
209         idr_remove(f->pvfs->idtree_fnum, f->fnum);
210
211         if (f->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
212                 if (unlink(f->name->full_name) != 0) {
213                         DEBUG(0,("pvfs_close: failed to delete '%s'\n", 
214                                  f->name->full_name));
215                 }
216         }
217
218         lck = odb_lock(f, f->pvfs->odb_context, &f->locking_key);
219         if (lck == NULL) {
220                 DEBUG(0,("Unable to lock opendb for close\n"));
221                 return 0;
222         }
223
224         status = odb_close_file(lck, f->fnum);
225         if (!NT_STATUS_IS_OK(status)) {
226                 DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n", 
227                          f->name->full_name, nt_errstr(status)));
228         }
229
230         talloc_free(lck);
231
232         return 0;
233 }
234
235
236 /*
237   form the lock context used for byte range locking and opendb
238   locking. Note that we must zero here to take account of
239   possible padding on some architectures
240 */
241 static NTSTATUS pvfs_locking_key(struct pvfs_filename *name, 
242                                  TALLOC_CTX *mem_ctx, DATA_BLOB *key)
243 {
244         struct {
245                 dev_t device;
246                 ino_t inode;
247         } lock_context;
248         ZERO_STRUCT(lock_context);
249
250         lock_context.device = name->st.st_dev;
251         lock_context.inode = name->st.st_ino;
252
253         *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
254         if (key->data == NULL) {
255                 return NT_STATUS_NO_MEMORY;
256         }
257         
258         return NT_STATUS_OK;
259 }
260
261
262 /*
263   create a new file
264 */
265 static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs, 
266                                  struct smbsrv_request *req, 
267                                  struct pvfs_filename *name, 
268                                  union smb_open *io)
269 {
270         struct pvfs_file *f;
271         NTSTATUS status;
272         int flags, fnum, fd;
273         struct odb_lock *lck;
274         uint32_t create_options = io->generic.in.create_options;
275         uint32_t share_access = io->generic.in.share_access;
276         uint32_t access_mask = io->generic.in.access_mask;
277         mode_t mode;
278
279         if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
280             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
281                 return NT_STATUS_CANNOT_DELETE;
282         }
283         
284         flags = O_RDWR;
285
286         f = talloc_p(req, struct pvfs_file);
287         if (f == NULL) {
288                 return NT_STATUS_NO_MEMORY;
289         }
290
291         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_NEW_FNUM, UINT16_MAX);
292         if (fnum == -1) {
293                 return NT_STATUS_TOO_MANY_OPENED_FILES;
294         }
295
296         if (io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) {
297                 mode = 0444;
298         } else {
299                 mode = 0644;
300         }
301
302         /* create the file */
303         fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
304         if (fd == -1) {
305                 idr_remove(pvfs->idtree_fnum, fnum);
306                 return pvfs_map_errno(pvfs, errno);
307         }
308
309         /* re-resolve the open fd */
310         status = pvfs_resolve_name_fd(pvfs, fd, name);
311         if (!NT_STATUS_IS_OK(status)) {
312                 idr_remove(pvfs->idtree_fnum, fnum);
313                 close(fd);
314                 return status;
315         }
316
317         /* form the lock context used for byte range locking and
318            opendb locking */
319         status = pvfs_locking_key(name, f, &f->locking_key);
320         if (!NT_STATUS_IS_OK(status)) {
321                 idr_remove(pvfs->idtree_fnum, fnum);
322                 close(fd);
323                 return status;
324         }
325
326         /* grab a lock on the open file record */
327         lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
328         if (lck == NULL) {
329                 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
330                          name->full_name));
331                 /* we were supposed to do a blocking lock, so something
332                    is badly wrong! */
333                 idr_remove(pvfs->idtree_fnum, fnum);
334                 close(fd);
335                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
336         }
337
338         status = odb_open_file(lck, fnum, share_access, create_options, access_mask);
339         if (!NT_STATUS_IS_OK(status)) {
340                 /* bad news, we must have hit a race */
341                 idr_remove(pvfs->idtree_fnum, fnum);
342                 close(fd);
343                 return status;
344         }
345
346         f->fnum = fnum;
347         f->fd = fd;
348         f->name = talloc_steal(f, name);
349         f->session = req->session;
350         f->smbpid = req->smbpid;
351         f->pvfs = pvfs;
352         f->pending_list = NULL;
353         f->lock_count = 0;
354         f->create_options = io->generic.in.create_options;
355         f->share_access = io->generic.in.share_access;
356         f->access_mask = io->generic.in.access_mask;
357
358         DLIST_ADD(pvfs->open_files, f);
359
360         /* setup a destructor to avoid file descriptor leaks on
361            abnormal termination */
362         talloc_set_destructor(f, pvfs_fd_destructor);
363
364         io->generic.out.oplock_level  = NO_OPLOCK;
365         io->generic.out.fnum          = f->fnum;
366         io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
367         io->generic.out.create_time   = name->dos.create_time;
368         io->generic.out.access_time   = name->dos.access_time;
369         io->generic.out.write_time    = name->dos.write_time;
370         io->generic.out.change_time   = name->dos.change_time;
371         io->generic.out.attrib        = name->dos.attrib;
372         io->generic.out.alloc_size    = name->dos.alloc_size;
373         io->generic.out.size          = name->st.st_size;
374         io->generic.out.file_type     = FILE_TYPE_DISK;
375         io->generic.out.ipc_state     = 0;
376         io->generic.out.is_directory  = 0;
377
378         /* success - keep the file handle */
379         talloc_steal(pvfs, f);
380
381         return NT_STATUS_OK;
382 }
383
384
385 /*
386   open a file
387 */
388 NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
389                    struct smbsrv_request *req, union smb_open *io)
390 {
391         struct pvfs_state *pvfs = ntvfs->private_data;
392         int fd, flags;
393         struct pvfs_filename *name;
394         struct pvfs_file *f;
395         NTSTATUS status;
396         int fnum;
397         struct odb_lock *lck;
398         uint32_t create_options;
399         uint32_t share_access;
400         uint32_t access_mask;
401
402         /* use the generic mapping code to avoid implementing all the
403            different open calls. This won't allow openx to work
404            perfectly as the mapping code has no way of knowing if two
405            opens are on the same connection, so this will need to
406            change eventually */    
407         if (io->generic.level != RAW_OPEN_GENERIC) {
408                 return ntvfs_map_open(req, io, ntvfs);
409         }
410
411         /* resolve the cifs name to a posix name */
412         status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
413                                    PVFS_RESOLVE_NO_WILDCARD, &name);
414         if (!NT_STATUS_IS_OK(status)) {
415                 return status;
416         }
417
418         /* directory opens are handled separately */
419         if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
420             (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
421                 return pvfs_open_directory(pvfs, req, name, io);
422         }
423
424         create_options = io->generic.in.create_options;
425         share_access   = io->generic.in.share_access;
426         access_mask    = io->generic.in.access_mask;
427
428         /* certain create options are not allowed */
429         if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
430             !(access_mask & STD_RIGHT_DELETE_ACCESS)) {
431                 return NT_STATUS_INVALID_PARAMETER;
432         }
433
434         switch (io->generic.in.open_disposition) {
435         case NTCREATEX_DISP_SUPERSEDE:
436                 if (!name->exists) {
437                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
438                 }
439                 flags = O_TRUNC;
440                 break;
441
442         case NTCREATEX_DISP_OVERWRITE_IF:
443                 flags = O_TRUNC;
444                 break;
445
446         case NTCREATEX_DISP_OPEN:
447                 if (!name->exists) {
448                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
449                 }
450                 flags = 0;
451                 break;
452
453         case NTCREATEX_DISP_OVERWRITE:
454                 if (!name->exists) {
455                         return NT_STATUS_OBJECT_NAME_NOT_FOUND;
456                 }
457                 flags = O_TRUNC;
458                 break;
459
460         case NTCREATEX_DISP_CREATE:
461                 if (name->exists) {
462                         return NT_STATUS_OBJECT_NAME_COLLISION;
463                 }
464                 flags = 0;
465                 break;
466
467         case NTCREATEX_DISP_OPEN_IF:
468                 flags = 0;
469                 break;
470
471         default:
472                 return NT_STATUS_INVALID_PARAMETER;
473         }
474
475         flags |= O_RDWR;
476
477         /* handle creating a new file separately */
478         if (!name->exists) {
479                 status = pvfs_create_file(pvfs, req, name, io);
480                 if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
481                         return status;
482                 }
483
484                 /* we've hit a race - the file was created during this call */
485                 if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
486                         return status;
487                 }
488
489                 /* try re-resolving the name */
490                 status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
491                                            PVFS_RESOLVE_NO_WILDCARD, &name);
492                 if (!NT_STATUS_IS_OK(status)) {
493                         return status;
494                 }
495                 /* fall through to a normal open */
496         }
497
498         if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
499             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
500                 return NT_STATUS_CANNOT_DELETE;
501         }
502
503         f = talloc_p(req, struct pvfs_file);
504         if (f == NULL) {
505                 return NT_STATUS_NO_MEMORY;
506         }
507
508         /* allocate a fnum */
509         fnum = idr_get_new_above(pvfs->idtree_fnum, f, PVFS_MIN_FILE_FNUM, UINT16_MAX);
510         if (fnum == -1) {
511                 return NT_STATUS_TOO_MANY_OPENED_FILES;
512         }
513
514         /* form the lock context used for byte range locking and
515            opendb locking */
516         status = pvfs_locking_key(name, f, &f->locking_key);
517         if (!NT_STATUS_IS_OK(status)) {
518                 idr_remove(pvfs->idtree_fnum, fnum);
519                 return status;
520         }
521
522         /* get a lock on this file before the actual open */
523         lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
524         if (lck == NULL) {
525                 DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
526                          name->full_name));
527                 /* we were supposed to do a blocking lock, so something
528                    is badly wrong! */
529                 idr_remove(pvfs->idtree_fnum, fnum);
530                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
531         }
532
533         /* see if we are allowed to open at the same time as existing opens */
534         status = odb_open_file(lck, fnum, share_access, create_options, access_mask);
535         if (!NT_STATUS_IS_OK(status)) {
536                 idr_remove(pvfs->idtree_fnum, fnum);
537                 return status;
538         }
539
540         f->fnum = fnum;
541         f->fd = -1;
542         f->name = talloc_steal(f, name);
543         f->session = req->session;
544         f->smbpid = req->smbpid;
545         f->pvfs = pvfs;
546         f->pending_list = NULL;
547         f->lock_count = 0;
548         f->create_options = io->generic.in.create_options;
549         f->share_access = io->generic.in.share_access;
550         f->access_mask = io->generic.in.access_mask;
551
552         DLIST_ADD(pvfs->open_files, f);
553
554         /* setup a destructor to avoid file descriptor leaks on
555            abnormal termination */
556         talloc_set_destructor(f, pvfs_fd_destructor);
557
558         /* do the actual open */
559         fd = open(name->full_name, flags);
560         if (fd == -1) {
561                 return pvfs_map_errno(pvfs, errno);
562         }
563
564         f->fd = fd;
565
566         /* re-resolve the open fd */
567         status = pvfs_resolve_name_fd(pvfs, fd, name);
568         if (!NT_STATUS_IS_OK(status)) {
569                 return status;
570         }
571
572         io->generic.out.oplock_level  = NO_OPLOCK;
573         io->generic.out.fnum          = f->fnum;
574         io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
575         io->generic.out.create_time   = name->dos.create_time;
576         io->generic.out.access_time   = name->dos.access_time;
577         io->generic.out.write_time    = name->dos.write_time;
578         io->generic.out.change_time   = name->dos.change_time;
579         io->generic.out.attrib        = name->dos.attrib;
580         io->generic.out.alloc_size    = name->dos.alloc_size;
581         io->generic.out.size          = name->st.st_size;
582         io->generic.out.file_type     = FILE_TYPE_DISK;
583         io->generic.out.ipc_state     = 0;
584         io->generic.out.is_directory  = 0;
585
586         /* success - keep the file handle */
587         talloc_steal(pvfs, f);
588
589         return NT_STATUS_OK;
590 }
591
592
593 /*
594   close a file
595 */
596 NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
597                     struct smbsrv_request *req, union smb_close *io)
598 {
599         struct pvfs_state *pvfs = ntvfs->private_data;
600         struct pvfs_file *f;
601         NTSTATUS status;
602
603         if (io->generic.level != RAW_CLOSE_CLOSE) {
604                 return ntvfs_map_close(req, io, ntvfs);
605         }
606
607         f = pvfs_find_fd(pvfs, req, io->close.in.fnum);
608         if (!f) {
609                 return NT_STATUS_INVALID_HANDLE;
610         }
611
612         if (f->fd != -1 && 
613             close(f->fd) == -1) {
614                 status = pvfs_map_errno(pvfs, errno);
615         } else {
616                 status = NT_STATUS_OK;
617         }
618         f->fd = -1;
619
620         /* the destructor takes care of the rest */
621         talloc_free(f);
622
623         return status;
624 }
625
626
627 /*
628   logoff - close all file descriptors open by a vuid
629 */
630 NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
631                      struct smbsrv_request *req)
632 {
633         struct pvfs_state *pvfs = ntvfs->private_data;
634         struct pvfs_file *f, *next;
635
636         for (f=pvfs->open_files;f;f=next) {
637                 next = f->next;
638                 if (f->session == req->session) {
639                         DLIST_REMOVE(pvfs->open_files, f);
640                         talloc_free(f);
641                 }
642         }
643
644         return NT_STATUS_OK;
645 }
646
647
648 /*
649   exit - close files for the current pid
650 */
651 NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
652                    struct smbsrv_request *req)
653 {
654         struct pvfs_state *pvfs = ntvfs->private_data;
655         struct pvfs_file *f, *next;
656
657         for (f=pvfs->open_files;f;f=next) {
658                 next = f->next;
659                 if (f->smbpid == req->smbpid) {
660                         DLIST_REMOVE(pvfs->open_files, f);
661                         talloc_free(f);
662                 }
663         }
664
665         return NT_STATUS_OK;
666 }
667
668
669 /*
670   change the create options on an already open file
671 */
672 NTSTATUS pvfs_change_create_options(struct pvfs_state *pvfs,
673                                     struct smbsrv_request *req, 
674                                     struct pvfs_file *f, uint32_t create_options)
675 {
676         struct odb_lock *lck;
677         NTSTATUS status;
678
679         if (f->create_options == create_options) {
680                 return NT_STATUS_OK;
681         }
682
683         if ((f->name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
684             (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
685                 return NT_STATUS_CANNOT_DELETE;
686         }
687
688         lck = odb_lock(req, pvfs->odb_context, &f->locking_key);
689         if (lck == NULL) {
690                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
691         }
692
693         status = odb_set_create_options(lck, f->fnum, create_options);
694         if (NT_STATUS_IS_OK(status)) {
695                 f->create_options = create_options;
696         }
697
698         return status;
699 }