r2613: use a talloc destructor to ensure that file descriptors are not leaked
[samba.git] / source / ntvfs / posix / pvfs_open.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    POSIX NTVFS backend - open and close
5
6    Copyright (C) Andrew Tridgell 2004
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "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, uint16_t fnum)
31 {
32         struct pvfs_file *f;
33         for (f=pvfs->open_files;f;f=f->next) {
34                 if (f->fnum == fnum) {
35                         return f;
36                 }
37         }
38         return NULL;
39 }
40
41 /*
42   by using a destructor we make sure that abnormal cleanup will not 
43   leak file descriptors (assuming at least the top level pointer is freed, which
44   will cascade down to here)
45 */
46 static int pvfs_fd_destructor(void *p)
47 {
48         struct pvfs_file *f = p;
49         if (f->fd != -1) {
50                 close(f->fd);
51                 f->fd = -1;
52         }
53         return 0;
54 }
55
56 /*
57   open a file
58   TODO: this is a temporary implementation derived from the simple backend
59   its purpose is to allow other tests to run 
60 */
61 NTSTATUS pvfs_open(struct smbsrv_request *req, union smb_open *io)
62 {
63         NTVFS_GET_PRIVATE(pvfs_state, pvfs, req);
64         int fd, flags;
65         struct pvfs_filename *name;
66         struct pvfs_file *f;
67         NTSTATUS status;
68
69         if (io->generic.level != RAW_OPEN_GENERIC) {
70                 return ntvfs_map_open(req, io, pvfs->ops);
71         }
72
73         /* resolve the cifs name to a posix name */
74         status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
75                                    PVFS_RESOLVE_NO_WILDCARD, &name);
76         if (!NT_STATUS_IS_OK(status)) {
77                 return status;
78         }
79
80         switch (io->generic.in.open_disposition) {
81         case NTCREATEX_DISP_SUPERSEDE:
82         case NTCREATEX_DISP_OVERWRITE_IF:
83                 flags = O_CREAT | O_TRUNC;
84                 break;
85         case NTCREATEX_DISP_OPEN:
86         case NTCREATEX_DISP_OVERWRITE:
87                 flags = 0;
88                 break;
89         case NTCREATEX_DISP_CREATE:
90                 flags = O_CREAT | O_EXCL;
91                 break;
92         case NTCREATEX_DISP_OPEN_IF:
93                 flags = O_CREAT;
94                 break;
95         default:
96                 flags = 0;
97                 break;
98         }
99         
100         flags |= O_RDWR;
101
102 /* we need to do this differently to support systems without O_DIRECTORY */
103 #ifndef O_DIRECTORY
104 #define O_DIRECTORY 0
105 #endif
106
107         if (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) {
108                 flags = O_RDONLY | O_DIRECTORY;
109                 if (pvfs->flags & PVFS_FLAG_READONLY) {
110                         goto do_open;
111                 }
112                 switch (io->generic.in.open_disposition) {
113                 case NTCREATEX_DISP_CREATE:
114                         if (mkdir(name->full_name, 0755) == -1) {
115                                 return pvfs_map_errno(pvfs,errno);
116                         }
117                         break;
118                 case NTCREATEX_DISP_OPEN_IF:
119                         if (mkdir(name->full_name, 0755) == -1 && errno != EEXIST) {
120                                 return pvfs_map_errno(pvfs,errno);
121                         }
122                         break;
123                 }
124         }
125
126 do_open:
127         fd = open(name->full_name, flags, 0644);
128         if (fd == -1) {
129                 if (errno == 0)
130                         errno = ENOENT;
131                 return pvfs_map_errno(pvfs,errno);
132         }
133
134         f = talloc_p(pvfs, struct pvfs_file);
135         if (f == NULL) {
136                 close(fd);
137                 return NT_STATUS_NO_MEMORY;
138         }
139
140         /* re-resolve the open fd */
141         status = pvfs_resolve_name_fd(pvfs, fd, name);
142         if (!NT_STATUS_IS_OK(status)) {
143                 return status;
144         }
145
146         f->fnum = fd;
147         f->fd = fd;
148         f->name = talloc_steal(f, name);
149
150         /* setup a destructor to avoid file descriptor leaks on
151            abnormal termination */
152         talloc_set_destructor(f, pvfs_fd_destructor);
153
154         DLIST_ADD(pvfs->open_files, f);
155
156         ZERO_STRUCT(io->generic.out);
157         
158         io->generic.out.create_time = name->dos.create_time;
159         io->generic.out.access_time = name->dos.access_time;
160         io->generic.out.write_time = name->dos.write_time;
161         io->generic.out.change_time = name->dos.change_time;
162         io->generic.out.fnum = f->fnum;
163         io->generic.out.alloc_size = name->dos.alloc_size;
164         io->generic.out.size = name->st.st_size;
165         io->generic.out.attrib = name->dos.attrib;
166         io->generic.out.is_directory = (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)?1:0;
167
168         return NT_STATUS_OK;
169 }
170
171
172 /*
173   close a file
174 */
175 NTSTATUS pvfs_close(struct smbsrv_request *req, union smb_close *io)
176 {
177         NTVFS_GET_PRIVATE(pvfs_state, pvfs, req);
178         struct pvfs_file *f;
179         NTSTATUS status;
180
181         if (io->generic.level != RAW_CLOSE_CLOSE) {
182                 /* we need a mapping function */
183                 return NT_STATUS_INVALID_LEVEL;
184         }
185
186         f = pvfs_find_fd(pvfs, io->close.in.fnum);
187         if (!f) {
188                 return NT_STATUS_INVALID_HANDLE;
189         }
190
191         if (close(f->fd) != 0) {
192                 status = pvfs_map_errno(pvfs, errno);
193         } else {
194                 status = NT_STATUS_OK;
195         }
196
197         talloc_set_destructor(f, NULL);
198
199         DLIST_REMOVE(pvfs->open_files, f);
200         talloc_free(f);
201
202         return status;
203 }
204