r4584: fix pvfs backend to pass the new enhanced RAW-ACLS test. Easy once I really the
[samba.git] / source / 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_size(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_new(req);
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                 goto failed;
145         }
146
147         /* make sure its matches the given attributes */
148         status = pvfs_match_attrib(pvfs, name1, attrib, 0);
149         if (!NT_STATUS_IS_OK(status)) {
150                 goto failed;
151         }
152
153         status = pvfs_can_rename(pvfs, name1);
154         if (!NT_STATUS_IS_OK(status)) {
155                 goto failed;
156         }
157
158         /* get a pvfs_filename dest object */
159         status = pvfs_resolve_partial(pvfs, mem_ctx, 
160                                       dir_path, fname2, &name2);
161         if (NT_STATUS_IS_OK(status)) {
162                 status = pvfs_can_delete(pvfs, req, name2);
163                 if (!NT_STATUS_IS_OK(status)) {
164                         goto failed;
165                 }
166         }
167
168         status = NT_STATUS_OK;
169
170         fname2 = talloc_asprintf(mem_ctx, "%s/%s", dir_path, fname2);
171         if (fname2 == NULL) {
172                 return NT_STATUS_NO_MEMORY;
173         }
174
175         if (rename(name1->full_name, fname2) == -1) {
176                 talloc_free(mem_ctx);
177                 return pvfs_map_errno(pvfs, errno);
178         }
179
180 failed:
181         talloc_free(mem_ctx);
182         return status;
183 }
184
185
186 /*
187   rename a set of files with wildcards
188 */
189 static NTSTATUS pvfs_rename_wildcard(struct pvfs_state *pvfs, 
190                                      struct smbsrv_request *req, 
191                                      union smb_rename *ren, 
192                                      struct pvfs_filename *name1, 
193                                      struct pvfs_filename *name2)
194 {
195         struct pvfs_dir *dir;
196         NTSTATUS status;
197         uint_t ofs = 0;
198         const char *fname, *fname2, *dir_path;
199         uint16_t attrib = ren->rename.in.attrib;
200         int total_renamed = 0;
201
202         /* get list of matching files */
203         status = pvfs_list_start(pvfs, name1, req, &dir);
204         if (!NT_STATUS_IS_OK(status)) {
205                 return status;
206         }
207
208         status = NT_STATUS_NO_SUCH_FILE;
209
210         dir_path = pvfs_list_unix_path(dir);
211
212         /* only allow wildcard renames within a directory */
213         if (strncmp(dir_path, name2->full_name, strlen(dir_path)) != 0 ||
214             name2->full_name[strlen(dir_path)] != '/' ||
215             strchr(name2->full_name + strlen(dir_path) + 1, '/')) {
216                 return NT_STATUS_INVALID_PARAMETER;
217         }
218
219         fname2 = talloc_strdup(name2, name2->full_name + strlen(dir_path) + 1);
220         if (fname2 == NULL) {
221                 return NT_STATUS_NO_MEMORY;
222         }
223
224         while ((fname = pvfs_list_next(dir, &ofs))) {
225                 status = pvfs_rename_one(pvfs, req, 
226                                          dir_path,
227                                          fname, fname2, attrib);
228                 if (NT_STATUS_IS_OK(status)) {
229                         total_renamed++;
230                 }
231         }
232
233         if (total_renamed == 0) {
234                 return status;
235         }
236
237         return NT_STATUS_OK;
238 }
239
240 /*
241   rename a set of files - SMBmv interface
242 */
243 static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs,
244                                struct smbsrv_request *req, union smb_rename *ren)
245 {
246         struct pvfs_state *pvfs = ntvfs->private_data;
247         NTSTATUS status;
248         struct pvfs_filename *name1, *name2;
249
250         /* resolve the cifs name to a posix name */
251         status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern1, 
252                                    PVFS_RESOLVE_WILDCARD, &name1);
253         if (!NT_STATUS_IS_OK(status)) {
254                 return status;
255         }
256
257         status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern2, 
258                                    PVFS_RESOLVE_WILDCARD, &name2);
259         if (!NT_STATUS_IS_OK(status)) {
260                 return status;
261         }
262
263         if (name1->has_wildcard || name2->has_wildcard) {
264                 return pvfs_rename_wildcard(pvfs, req, ren, name1, name2);
265         }
266
267         if (!name1->exists) {
268                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
269         }
270
271         if (strcmp(name1->full_name, name2->full_name) == 0) {
272                 return NT_STATUS_OK;
273         }
274
275         if (name2->exists) {
276                 return NT_STATUS_OBJECT_NAME_COLLISION;
277         }
278
279         status = pvfs_match_attrib(pvfs, name1, ren->rename.in.attrib, 0);
280         if (!NT_STATUS_IS_OK(status)) {
281                 return status;
282         }
283
284         status = pvfs_access_check_create_nomask(pvfs, req, name2);
285         if (!NT_STATUS_IS_OK(status)) {
286                 return status;
287         }
288
289         status = pvfs_can_rename(pvfs, name1);
290         if (!NT_STATUS_IS_OK(status)) {
291                 return status;
292         }
293
294         if (rename(name1->full_name, name2->full_name) == -1) {
295                 return pvfs_map_errno(pvfs, errno);
296         }
297         
298         return NT_STATUS_OK;
299 }
300
301
302 /*
303   rename a set of files - ntrename interface
304 */
305 static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
306                                struct smbsrv_request *req, union smb_rename *ren)
307 {
308         struct pvfs_state *pvfs = ntvfs->private_data;
309         NTSTATUS status;
310         struct pvfs_filename *name1, *name2;
311
312         switch (ren->ntrename.in.flags) {
313         case RENAME_FLAG_RENAME:
314         case RENAME_FLAG_HARD_LINK:
315         case RENAME_FLAG_COPY:
316         case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
317                 break;
318         default:
319                 return NT_STATUS_ACCESS_DENIED;
320         }
321
322         /* resolve the cifs name to a posix name */
323         status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name, 
324                                    PVFS_RESOLVE_WILDCARD, &name1);
325         if (!NT_STATUS_IS_OK(status)) {
326                 return status;
327         }
328
329         status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name, 
330                                    PVFS_RESOLVE_WILDCARD, &name2);
331         if (!NT_STATUS_IS_OK(status)) {
332                 return status;
333         }
334
335         if (name1->has_wildcard || name2->has_wildcard) {
336                 return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
337         }
338
339         if (!name1->exists) {
340                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
341         }
342
343         if (strcmp(name1->full_name, name2->full_name) == 0) {
344                 return NT_STATUS_OK;
345         }
346
347         if (name2->exists) {
348                 return NT_STATUS_OBJECT_NAME_COLLISION;
349         }
350
351         status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
352         if (!NT_STATUS_IS_OK(status)) {
353                 return status;
354         }
355
356         status = pvfs_can_rename(pvfs, name1);
357         if (!NT_STATUS_IS_OK(status)) {
358                 return status;
359         }
360
361         switch (ren->ntrename.in.flags) {
362         case RENAME_FLAG_RENAME:
363                 status = pvfs_access_check_create_nomask(pvfs, req, name2);
364                 if (!NT_STATUS_IS_OK(status)) {
365                         return status;
366                 }
367                 if (rename(name1->full_name, name2->full_name) == -1) {
368                         return pvfs_map_errno(pvfs, errno);
369                 }
370                 break;
371
372         case RENAME_FLAG_HARD_LINK:
373                 status = pvfs_access_check_create_nomask(pvfs, req, name2);
374                 if (!NT_STATUS_IS_OK(status)) {
375                         return status;
376                 }
377                 if (link(name1->full_name, name2->full_name) == -1) {
378                         return pvfs_map_errno(pvfs, errno);
379                 }
380                 break;
381
382         case RENAME_FLAG_COPY:
383                 status = pvfs_access_check_create_nomask(pvfs, req, name2);
384                 if (!NT_STATUS_IS_OK(status)) {
385                         return status;
386                 }
387                 return pvfs_copy_file(pvfs, name1, name2);
388
389         case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
390                 return NT_STATUS_INVALID_PARAMETER;
391
392         default:
393                 return NT_STATUS_ACCESS_DENIED;
394         }
395
396         
397         return NT_STATUS_OK;
398 }
399
400 /*
401   rename a set of files - ntrename interface
402 */
403 NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
404                      struct smbsrv_request *req, union smb_rename *ren)
405 {
406         switch (ren->generic.level) {
407         case RAW_RENAME_RENAME:
408                 return pvfs_rename_mv(ntvfs, req, ren);
409
410         case RAW_RENAME_NTRENAME:
411                 return pvfs_rename_nt(ntvfs, req, ren);
412
413         default:
414                 break;
415         }
416
417         return NT_STATUS_INVALID_LEVEL;
418 }
419