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