r4408: added the remaining access check hooks into pvfs. All calls should now have...
[samba.git] / source4 / ntvfs / posix / pvfs_rename.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    POSIX NTVFS backend - rename
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
26
27 /*
28   resolve a wildcard rename pattern. This works on one component of the name
29 */
30 static const char *pvfs_resolve_wildcard_component(TALLOC_CTX *mem_ctx, 
31                                                    const char *fname, 
32                                                    const char *pattern)
33 {
34         const char *p1, *p2;
35         char *dest, *d;
36
37         /* the length is bounded by the length of the two strings combined */
38         dest = talloc(mem_ctx, strlen(fname) + strlen(pattern) + 1);
39         if (dest == NULL) {
40                 return NULL;
41         }
42
43         p1 = fname;
44         p2 = pattern;
45         d = dest;
46
47         while (*p2) {
48                 codepoint_t c1, c2;
49                 size_t c_size1, c_size2;
50                 c1 = next_codepoint(p1, &c_size1);
51                 c2 = next_codepoint(p2, &c_size2);
52                 if (c2 == '?') {
53                         d += push_codepoint(d, c1);
54                 } else if (c2 == '*') {
55                         memcpy(d, p1, strlen(p1));
56                         d += strlen(p1);
57                         break;
58                 } else {
59                         d += push_codepoint(d, c2);
60                 }
61
62                 p1 += c_size1;
63                 p2 += c_size2;
64         }
65
66         *d = 0;
67
68         return dest;
69 }
70
71 /*
72   resolve a wildcard rename pattern.
73 */
74 static const char *pvfs_resolve_wildcard(TALLOC_CTX *mem_ctx, 
75                                          const char *fname, 
76                                          const char *pattern)
77 {
78         const char *base1, *base2;
79         const char *ext1, *ext2;
80         char *p;
81
82         /* break into base part plus extension */
83         p = strrchr_m(fname, '.');
84         if (p == NULL) {
85                 ext1 = "";
86                 base1 = fname;
87         } else {
88                 ext1 = talloc_strdup(mem_ctx, p+1);
89                 base1 = talloc_strndup(mem_ctx, fname, p-fname);
90         }
91         if (ext1 == NULL || base1 == NULL) {
92                 return NULL;
93         }
94
95         p = strrchr_m(pattern, '.');
96         if (p == NULL) {
97                 ext2 = "";
98                 base2 = fname;
99         } else {
100                 ext2 = talloc_strdup(mem_ctx, p+1);
101                 base2 = talloc_strndup(mem_ctx, pattern, p-pattern);
102         }
103         if (ext2 == NULL || base2 == NULL) {
104                 return NULL;
105         }
106
107         base1 = pvfs_resolve_wildcard_component(mem_ctx, base1, base2);
108         ext1 = pvfs_resolve_wildcard_component(mem_ctx, ext1, ext2);
109         if (base1 == NULL || ext1 == NULL) {
110                 return NULL;
111         }
112
113         if (*ext1 == 0) {
114                 return base1;
115         }
116
117         return talloc_asprintf(mem_ctx, "%s.%s", base1, ext1);
118 }
119
120 /*
121   rename one file from a wildcard set
122 */
123 static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs, 
124                                 struct smbsrv_request *req, 
125                                 const char *dir_path,
126                                 const char *fname1,
127                                 const char *fname2,
128                                 uint16_t attrib)
129 {
130         struct pvfs_filename *name1, *name2;
131         TALLOC_CTX *mem_ctx = talloc(req, 0);
132         NTSTATUS status;
133
134         /* resolve the wildcard pattern for this name */
135         fname2 = pvfs_resolve_wildcard(mem_ctx, fname1, fname2);
136         if (fname2 == NULL) {
137                 return NT_STATUS_NO_MEMORY;
138         }
139
140         /* get a pvfs_filename source object */
141         status = pvfs_resolve_partial(pvfs, mem_ctx, 
142                                       dir_path, fname1, &name1);
143         if (!NT_STATUS_IS_OK(status)) {
144                 talloc_free(mem_ctx);
145                 return status;
146         }
147
148         /* make sure its matches the given attributes */
149         status = pvfs_match_attrib(pvfs, name1, attrib, 0);
150         if (!NT_STATUS_IS_OK(status)) {
151                 talloc_free(mem_ctx);
152                 return status;
153         }
154
155         status = pvfs_can_rename(pvfs, name1);
156         if (!NT_STATUS_IS_OK(status)) {
157                 talloc_free(mem_ctx);
158                 return status;
159         }
160
161         /* get a pvfs_filename dest object */
162         status = pvfs_resolve_partial(pvfs, mem_ctx, 
163                                       dir_path, fname2, &name2);
164         if (NT_STATUS_IS_OK(status)) {
165                 status = pvfs_can_delete(pvfs, req, name2);
166                 if (!NT_STATUS_IS_OK(status)) {
167                         talloc_free(mem_ctx);
168                         return status;
169                 }
170         }
171
172         status = pvfs_access_check_create(pvfs, req, name2);
173         if (!NT_STATUS_IS_OK(status)) {
174                 return status;
175         }
176
177         fname2 = talloc_asprintf(mem_ctx, "%s/%s", dir_path, fname2);
178         if (fname2 == NULL) {
179                 return NT_STATUS_NO_MEMORY;
180         }
181
182         if (rename(name1->full_name, fname2) == -1) {
183                 talloc_free(mem_ctx);
184                 return pvfs_map_errno(pvfs, errno);
185         }
186
187         talloc_free(mem_ctx);
188
189         return NT_STATUS_OK;
190 }
191
192
193 /*
194   rename a set of files with wildcards
195 */
196 static NTSTATUS pvfs_rename_wildcard(struct pvfs_state *pvfs, 
197                                      struct smbsrv_request *req, 
198                                      union smb_rename *ren, 
199                                      struct pvfs_filename *name1, 
200                                      struct pvfs_filename *name2)
201 {
202         struct pvfs_dir *dir;
203         NTSTATUS status;
204         uint_t ofs = 0;
205         const char *fname, *fname2, *dir_path;
206         uint16_t attrib = ren->rename.in.attrib;
207         int total_renamed = 0;
208
209         /* get list of matching files */
210         status = pvfs_list_start(pvfs, name1, req, &dir);
211         if (!NT_STATUS_IS_OK(status)) {
212                 return status;
213         }
214
215         status = NT_STATUS_NO_SUCH_FILE;
216
217         dir_path = pvfs_list_unix_path(dir);
218
219         /* only allow wildcard renames within a directory */
220         if (strncmp(dir_path, name2->full_name, strlen(dir_path)) != 0 ||
221             name2->full_name[strlen(dir_path)] != '/' ||
222             strchr(name2->full_name + strlen(dir_path) + 1, '/')) {
223                 return NT_STATUS_INVALID_PARAMETER;
224         }
225
226         fname2 = talloc_strdup(name2, name2->full_name + strlen(dir_path) + 1);
227         if (fname2 == NULL) {
228                 return NT_STATUS_NO_MEMORY;
229         }
230
231         while ((fname = pvfs_list_next(dir, &ofs))) {
232                 status = pvfs_rename_one(pvfs, req, 
233                                          dir_path,
234                                          fname, fname2, attrib);
235                 if (NT_STATUS_IS_OK(status)) {
236                         total_renamed++;
237                 }
238         }
239
240         if (total_renamed == 0) {
241                 return status;
242         }
243
244         return NT_STATUS_OK;
245 }
246
247 /*
248   rename a set of files - SMBmv interface
249 */
250 static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs,
251                                struct smbsrv_request *req, union smb_rename *ren)
252 {
253         struct pvfs_state *pvfs = ntvfs->private_data;
254         NTSTATUS status;
255         struct pvfs_filename *name1, *name2;
256
257         /* resolve the cifs name to a posix name */
258         status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern1, 
259                                    PVFS_RESOLVE_WILDCARD, &name1);
260         if (!NT_STATUS_IS_OK(status)) {
261                 return status;
262         }
263
264         status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern2, 
265                                    PVFS_RESOLVE_WILDCARD, &name2);
266         if (!NT_STATUS_IS_OK(status)) {
267                 return status;
268         }
269
270         if (name1->has_wildcard || name2->has_wildcard) {
271                 return pvfs_rename_wildcard(pvfs, req, ren, name1, name2);
272         }
273
274         if (!name1->exists) {
275                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
276         }
277
278         if (strcmp(name1->full_name, name2->full_name) == 0) {
279                 return NT_STATUS_OK;
280         }
281
282         if (name2->exists) {
283                 return NT_STATUS_OBJECT_NAME_COLLISION;
284         }
285
286         status = pvfs_match_attrib(pvfs, name1, ren->rename.in.attrib, 0);
287         if (!NT_STATUS_IS_OK(status)) {
288                 return status;
289         }
290
291         status = pvfs_access_check_create(pvfs, req, name2);
292         if (!NT_STATUS_IS_OK(status)) {
293                 return status;
294         }
295
296         status = pvfs_can_rename(pvfs, name1);
297         if (!NT_STATUS_IS_OK(status)) {
298                 return status;
299         }
300
301         if (rename(name1->full_name, name2->full_name) == -1) {
302                 return pvfs_map_errno(pvfs, errno);
303         }
304         
305         return NT_STATUS_OK;
306 }
307
308
309 /*
310   rename a set of files - ntrename interface
311 */
312 static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
313                                struct smbsrv_request *req, union smb_rename *ren)
314 {
315         struct pvfs_state *pvfs = ntvfs->private_data;
316         NTSTATUS status;
317         struct pvfs_filename *name1, *name2;
318
319         switch (ren->ntrename.in.flags) {
320         case RENAME_FLAG_RENAME:
321         case RENAME_FLAG_HARD_LINK:
322         case RENAME_FLAG_COPY:
323         case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
324                 break;
325         default:
326                 return NT_STATUS_ACCESS_DENIED;
327         }
328
329         /* resolve the cifs name to a posix name */
330         status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name, 
331                                    PVFS_RESOLVE_WILDCARD, &name1);
332         if (!NT_STATUS_IS_OK(status)) {
333                 return status;
334         }
335
336         status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name, 
337                                    PVFS_RESOLVE_WILDCARD, &name2);
338         if (!NT_STATUS_IS_OK(status)) {
339                 return status;
340         }
341
342         if (name1->has_wildcard || name2->has_wildcard) {
343                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
344         }
345
346         if (!name1->exists) {
347                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
348         }
349
350         if (strcmp(name1->full_name, name2->full_name) == 0) {
351                 return NT_STATUS_OK;
352         }
353
354         if (name2->exists) {
355                 return NT_STATUS_OBJECT_NAME_COLLISION;
356         }
357
358         status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
359         if (!NT_STATUS_IS_OK(status)) {
360                 return status;
361         }
362
363         status = pvfs_can_rename(pvfs, name1);
364         if (!NT_STATUS_IS_OK(status)) {
365                 return status;
366         }
367
368         switch (ren->ntrename.in.flags) {
369         case RENAME_FLAG_RENAME:
370                 status = pvfs_access_check_create(pvfs, req, name2);
371                 if (!NT_STATUS_IS_OK(status)) {
372                         return status;
373                 }
374                 if (rename(name1->full_name, name2->full_name) == -1) {
375                         return pvfs_map_errno(pvfs, errno);
376                 }
377                 break;
378
379         case RENAME_FLAG_HARD_LINK:
380                 status = pvfs_access_check_create(pvfs, req, name2);
381                 if (!NT_STATUS_IS_OK(status)) {
382                         return status;
383                 }
384                 if (link(name1->full_name, name2->full_name) == -1) {
385                         return pvfs_map_errno(pvfs, errno);
386                 }
387                 break;
388
389         case RENAME_FLAG_COPY:
390                 status = pvfs_access_check_create(pvfs, req, name2);
391                 if (!NT_STATUS_IS_OK(status)) {
392                         return status;
393                 }
394                 return pvfs_copy_file(pvfs, name1, name2);
395
396         case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
397                 return NT_STATUS_INVALID_PARAMETER;
398
399         default:
400                 return NT_STATUS_ACCESS_DENIED;
401         }
402
403         
404         return NT_STATUS_OK;
405 }
406
407 /*
408   rename a set of files - ntrename interface
409 */
410 NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
411                      struct smbsrv_request *req, union smb_rename *ren)
412 {
413         switch (ren->generic.level) {
414         case RAW_RENAME_RENAME:
415                 return pvfs_rename_mv(ntvfs, req, ren);
416
417         case RAW_RENAME_NTRENAME:
418                 return pvfs_rename_nt(ntvfs, req, ren);
419
420         default:
421                 break;
422         }
423
424         return NT_STATUS_INVALID_LEVEL;
425 }
426