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