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