r3729: permission changes on directories always include the FILE_ATTRIBUTE_DIRECTORY bit
[sfrench/samba-autobuild/.git] / source4 / ntvfs / posix / pvfs_setfileinfo.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    POSIX NTVFS backend - setfileinfo
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 #include "system/time.h"
26 #include "librpc/gen_ndr/ndr_xattr.h"
27
28
29 /*
30   rename_information level
31 */
32 static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs, 
33                                         struct smbsrv_request *req, 
34                                         struct pvfs_filename *name,
35                                         struct smb_rename_information *r)
36 {
37         NTSTATUS status;
38         struct pvfs_filename *name2;
39         char *new_name, *p;
40
41         /* renames are only allowed within a directory */
42         if (strchr_m(r->new_name, '\\')) {
43                 return NT_STATUS_NOT_SUPPORTED;
44         }
45
46         if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
47                 /* don't allow this for now */
48                 return NT_STATUS_FILE_IS_A_DIRECTORY;
49         }
50
51         /* w2k3 does not appear to allow relative rename */
52         if (r->root_fid != 0) {
53                 return NT_STATUS_INVALID_PARAMETER;
54         }
55
56         /* construct the fully qualified windows name for the new file name */
57         new_name = talloc_strdup(req, name->original_name);
58         if (new_name == NULL) {
59                 return NT_STATUS_NO_MEMORY;
60         }
61         p = strrchr_m(new_name, '\\');
62         if (p == NULL) {
63                 return NT_STATUS_OBJECT_NAME_INVALID;
64         }
65         *p = 0;
66
67         new_name = talloc_asprintf(req, "%s\\%s", new_name, r->new_name);
68         if (new_name == NULL) {
69                 return NT_STATUS_NO_MEMORY;
70         }
71
72         /* resolve the new name */
73         status = pvfs_resolve_name(pvfs, name, new_name, PVFS_RESOLVE_NO_WILDCARD, &name2);
74         if (!NT_STATUS_IS_OK(status)) {
75                 return status;
76         }
77
78         /* if the destination exists, then check the rename is allowed */
79         if (name2->exists) {
80                 if (strcmp(name2->full_name, name->full_name) == 0) {
81                         /* rename to same name is null-op */
82                         return NT_STATUS_OK;
83                 }
84
85                 if (!r->overwrite) {
86                         return NT_STATUS_OBJECT_NAME_COLLISION;
87                 }
88
89                 status = pvfs_can_delete(pvfs, name2);
90                 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
91                         return NT_STATUS_ACCESS_DENIED;
92                 }
93                 if (!NT_STATUS_IS_OK(status)) {
94                         return status;
95                 }
96         }
97
98         if (rename(name->full_name, name2->full_name) == -1) {
99                 return pvfs_map_errno(pvfs, errno);
100         }
101
102         name->full_name = talloc_steal(name, name2->full_name);
103         name->original_name = talloc_steal(name, name2->original_name);
104
105         return NT_STATUS_OK;
106 }
107
108 /*
109   add a single DOS EA
110 */
111 static NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs, 
112                                         struct pvfs_filename *name,
113                                         int fd, struct ea_struct *ea)
114 {
115         struct xattr_DosEAs *ealist = talloc_p(pvfs, struct xattr_DosEAs);
116         int i;
117         NTSTATUS status;
118
119         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
120                 return NT_STATUS_NOT_SUPPORTED;
121         }
122
123         /* load the current list */
124         status = pvfs_doseas_load(pvfs, name, fd, ealist);
125         if (!NT_STATUS_IS_OK(status)) {
126                 return status;
127         }
128
129         /* see if its already there */
130         for (i=0;i<ealist->num_eas;i++) {
131                 if (StrCaseCmp(ealist->eas[i].name, ea->name.s) == 0) {
132                         ealist->eas[i].value = ea->value;
133                         goto save;
134                 }
135         }
136
137         /* add it */
138         ealist->eas = talloc_realloc_p(ealist, ealist->eas, struct xattr_EA, ealist->num_eas+1);
139         if (ealist->eas == NULL) {
140                 return NT_STATUS_NO_MEMORY;
141         }
142         ealist->eas[i].name = ea->name.s;
143         ealist->eas[i].value = ea->value;
144         ealist->num_eas++;
145         
146 save:
147         status = pvfs_doseas_save(pvfs, name, fd, ealist);
148         if (!NT_STATUS_IS_OK(status)) {
149                 return status;
150         }
151
152         name->dos.ea_size = 4;
153         for (i=0;i<ealist->num_eas;i++) {
154                 name->dos.ea_size += 4 + strlen(ealist->eas[i].name)+1 + 
155                         ealist->eas[i].value.length;
156         }
157
158         /* update the ea_size attrib */
159         return pvfs_dosattrib_save(pvfs, name, fd);
160 }
161
162 /*
163   set info on a open file
164 */
165 NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
166                           struct smbsrv_request *req, 
167                           union smb_setfileinfo *info)
168 {
169         struct pvfs_state *pvfs = ntvfs->private_data;
170         struct utimbuf unix_times;
171         struct pvfs_file *f;
172         struct pvfs_file_handle *h;
173         uint32_t create_options;
174         struct pvfs_filename newstats;
175         NTSTATUS status;
176
177         f = pvfs_find_fd(pvfs, req, info->generic.file.fnum);
178         if (!f) {
179                 return NT_STATUS_INVALID_HANDLE;
180         }
181
182         h = f->handle;
183
184         /* update the file information */
185         status = pvfs_resolve_name_fd(pvfs, h->fd, h->name);
186         if (!NT_STATUS_IS_OK(status)) {
187                 return status;
188         }
189
190         /* we take a copy of the current file stats, then update
191            newstats in each of the elements below. At the end we
192            compare, and make any changes needed */
193         newstats = *h->name;
194
195         switch (info->generic.level) {
196         case RAW_SFILEINFO_SETATTR:
197                 if (!null_time(info->setattr.in.write_time)) {
198                         unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
199                 }
200                 if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
201                         newstats.dos.attrib = info->setattr.in.attrib;
202                 }
203                 break;
204
205         case RAW_SFILEINFO_SETATTRE:
206         case RAW_SFILEINFO_STANDARD:
207                 if (!null_time(info->setattre.in.create_time)) {
208                         unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
209                 }
210                 if (!null_time(info->setattre.in.access_time)) {
211                         unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
212                 }
213                 if (!null_time(info->setattre.in.write_time)) {
214                         unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
215                 }
216                 break;
217
218         case RAW_SFILEINFO_EA_SET:
219                 return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd, 
220                                                &info->ea_set.in.ea);
221
222         case RAW_SFILEINFO_BASIC_INFO:
223         case RAW_SFILEINFO_BASIC_INFORMATION:
224                 if (info->basic_info.in.create_time) {
225                         newstats.dos.create_time = info->basic_info.in.create_time;
226                 }
227                 if (info->basic_info.in.access_time) {
228                         newstats.dos.access_time = info->basic_info.in.access_time;
229                 }
230                 if (info->basic_info.in.write_time) {
231                         newstats.dos.write_time = info->basic_info.in.write_time;
232                 }
233                 if (info->basic_info.in.change_time) {
234                         newstats.dos.change_time = info->basic_info.in.change_time;
235                 }
236                 if (info->basic_info.in.attrib != 0) {
237                         newstats.dos.attrib = info->basic_info.in.attrib;
238                 }
239                 break;
240
241         case RAW_SFILEINFO_DISPOSITION_INFO:
242         case RAW_SFILEINFO_DISPOSITION_INFORMATION:
243                 if (!(f->access_mask & STD_RIGHT_DELETE_ACCESS)) {
244                         return NT_STATUS_ACCESS_DENIED;
245                 }
246                 create_options = h->create_options;
247                 if (info->disposition_info.in.delete_on_close) {
248                         create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
249                 } else {
250                         create_options &= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
251                 }
252                 return pvfs_change_create_options(pvfs, req, f, create_options);
253
254         case RAW_SFILEINFO_ALLOCATION_INFO:
255         case RAW_SFILEINFO_ALLOCATION_INFORMATION:
256                 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
257                 if (newstats.dos.alloc_size < newstats.st.st_size) {
258                         newstats.st.st_size = newstats.dos.alloc_size;
259                 }
260                 break;
261
262         case RAW_SFILEINFO_END_OF_FILE_INFO:
263         case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
264                 newstats.st.st_size = info->end_of_file_info.in.size;
265                 break;
266
267         case RAW_SFILEINFO_POSITION_INFORMATION:
268                 h->position = info->position_information.in.position;
269                 break;
270
271         case RAW_SFILEINFO_MODE_INFORMATION:
272                 /* this one is a puzzle */
273                 if (info->mode_information.in.mode != 0 &&
274                     info->mode_information.in.mode != 2 &&
275                     info->mode_information.in.mode != 4 &&
276                     info->mode_information.in.mode != 6) {
277                         return NT_STATUS_INVALID_PARAMETER;
278                 }
279                 h->mode = info->mode_information.in.mode;
280                 break;
281
282         case RAW_SFILEINFO_RENAME_INFORMATION:
283                 return pvfs_setfileinfo_rename(pvfs, req, h->name, 
284                                                &info->rename_information.in);
285
286         default:
287                 return NT_STATUS_INVALID_LEVEL;
288         }
289
290         /* possibly change the file size */
291         if (newstats.st.st_size != h->name->st.st_size) {
292                 int ret;
293                 if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
294                         return NT_STATUS_FILE_IS_A_DIRECTORY;
295                 }
296                 if (f->access_mask & SA_RIGHT_FILE_WRITE_APPEND) {
297                         ret = ftruncate(h->fd, newstats.st.st_size);
298                 } else {
299                         ret = truncate(h->name->full_name, newstats.st.st_size);
300                 }
301                 if (ret == -1) {
302                         return pvfs_map_errno(pvfs, errno);
303                 }
304         }
305
306         /* possibly change the file timestamps */
307         ZERO_STRUCT(unix_times);
308         if (newstats.dos.access_time != h->name->dos.access_time) {
309                 unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
310         }
311         if (newstats.dos.write_time != h->name->dos.write_time) {
312                 unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
313         }
314         if (unix_times.actime != 0 || unix_times.modtime != 0) {
315                 if (utime(h->name->full_name, &unix_times) == -1) {
316                         return pvfs_map_errno(pvfs, errno);
317                 }
318         }
319
320         /* possibly change the attribute */
321         if (newstats.dos.attrib != h->name->dos.attrib) {
322                 mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
323                 if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
324                         /* ignore on directories for now */
325                         return NT_STATUS_OK;
326                 }
327                 if (fchmod(h->fd, mode) == -1) {
328                         return pvfs_map_errno(pvfs, errno);
329                 }
330         }
331
332         *h->name = newstats;
333
334         return pvfs_dosattrib_save(pvfs, h->name, h->fd);
335 }
336
337
338 /*
339   set info on a pathname
340 */
341 NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
342                           struct smbsrv_request *req, union smb_setfileinfo *info)
343 {
344         struct pvfs_state *pvfs = ntvfs->private_data;
345         struct pvfs_filename *name;
346         struct pvfs_filename newstats;
347         NTSTATUS status;
348         struct utimbuf unix_times;
349
350         /* resolve the cifs name to a posix name */
351         status = pvfs_resolve_name(pvfs, req, info->generic.file.fname, 
352                                    PVFS_RESOLVE_NO_WILDCARD, &name);
353         if (!NT_STATUS_IS_OK(status)) {
354                 return status;
355         }
356
357         if (!name->exists) {
358                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
359         }
360
361
362         /* we take a copy of the current file stats, then update
363            newstats in each of the elements below. At the end we
364            compare, and make any changes needed */
365         newstats = *name;
366
367         switch (info->generic.level) {
368         case RAW_SFILEINFO_SETATTR:
369                 if (!null_time(info->setattr.in.write_time)) {
370                         unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
371                 }
372                 if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
373                         newstats.dos.attrib = info->setattr.in.attrib;
374                 }
375                 break;
376
377         case RAW_SFILEINFO_SETATTRE:
378         case RAW_SFILEINFO_STANDARD:
379                 if (!null_time(info->setattre.in.create_time)) {
380                         unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
381                 }
382                 if (!null_time(info->setattre.in.access_time)) {
383                         unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
384                 }
385                 if (!null_time(info->setattre.in.write_time)) {
386                         unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
387                 }
388                 break;
389
390         case RAW_SFILEINFO_EA_SET:
391                 return pvfs_setfileinfo_ea_set(pvfs, name, -1, &info->ea_set.in.ea);
392
393         case RAW_SFILEINFO_BASIC_INFO:
394         case RAW_SFILEINFO_BASIC_INFORMATION:
395                 if (info->basic_info.in.create_time) {
396                         newstats.dos.create_time = info->basic_info.in.create_time;
397                 }
398                 if (info->basic_info.in.access_time) {
399                         newstats.dos.access_time = info->basic_info.in.access_time;
400                 }
401                 if (info->basic_info.in.write_time) {
402                         newstats.dos.write_time = info->basic_info.in.write_time;
403                 }
404                 if (info->basic_info.in.change_time) {
405                         newstats.dos.change_time = info->basic_info.in.change_time;
406                 }
407                 if (info->basic_info.in.attrib != 0) {
408                         newstats.dos.attrib = info->basic_info.in.attrib;
409                 }
410                 break;
411
412         case RAW_SFILEINFO_ALLOCATION_INFO:
413         case RAW_SFILEINFO_ALLOCATION_INFORMATION:
414                 if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
415                         /* strange. Increasing the allocation size via setpathinfo 
416                            should be silently ignored */
417                         break;
418                 }
419                 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
420                 if (newstats.dos.alloc_size < newstats.st.st_size) {
421                         newstats.st.st_size = newstats.dos.alloc_size;
422                 }
423                 break;
424
425         case RAW_SFILEINFO_END_OF_FILE_INFO:
426         case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
427                 newstats.st.st_size = info->end_of_file_info.in.size;
428                 break;
429
430         case RAW_SFILEINFO_MODE_INFORMATION:
431                 if (info->mode_information.in.mode != 0 &&
432                     info->mode_information.in.mode != 2 &&
433                     info->mode_information.in.mode != 4 &&
434                     info->mode_information.in.mode != 6) {
435                         return NT_STATUS_INVALID_PARAMETER;
436                 }
437                 return NT_STATUS_OK;
438
439         case RAW_SFILEINFO_RENAME_INFORMATION:
440                 return pvfs_setfileinfo_rename(pvfs, req, name, 
441                                                &info->rename_information.in);
442
443         case RAW_SFILEINFO_DISPOSITION_INFO:
444         case RAW_SFILEINFO_DISPOSITION_INFORMATION:
445         case RAW_SFILEINFO_POSITION_INFORMATION:
446                 return NT_STATUS_OK;
447
448         default:
449                 return NT_STATUS_INVALID_LEVEL;
450         }
451
452         /* possibly change the file size */
453         if (newstats.st.st_size != name->st.st_size) {
454                 if (truncate(name->full_name, newstats.st.st_size) == -1) {
455                         return pvfs_map_errno(pvfs, errno);
456                 }
457         }
458
459         /* possibly change the file timestamps */
460         ZERO_STRUCT(unix_times);
461         if (newstats.dos.access_time != name->dos.access_time) {
462                 unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
463         }
464         if (newstats.dos.write_time != name->dos.write_time) {
465                 unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
466         }
467         if (unix_times.actime != 0 || unix_times.modtime != 0) {
468                 if (utime(name->full_name, &unix_times) == -1) {
469                         return pvfs_map_errno(pvfs, errno);
470                 }
471         }
472
473         /* possibly change the attribute */
474         newstats.dos.attrib |= (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY);
475         if (newstats.dos.attrib != name->dos.attrib) {
476                 mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
477                 if (chmod(name->full_name, mode) == -1) {
478                         return pvfs_map_errno(pvfs, errno);
479                 }
480         }
481
482         *name = newstats;
483
484         return pvfs_dosattrib_save(pvfs, name, -1);
485 }
486