31db6ce630647f85388ba7d839585c2c4164613d
[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                         if (fchmod(h->fd, mode) == -1) {
370                                 return pvfs_map_errno(pvfs, errno);
371                         }
372                 }
373         }
374
375         *h->name = newstats;
376
377         return pvfs_dosattrib_save(pvfs, h->name, h->fd);
378 }
379
380
381 /*
382   set info on a pathname
383 */
384 NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
385                           struct smbsrv_request *req, union smb_setfileinfo *info)
386 {
387         struct pvfs_state *pvfs = ntvfs->private_data;
388         struct pvfs_filename *name;
389         struct pvfs_filename newstats;
390         NTSTATUS status;
391         struct utimbuf unix_times;
392
393         /* resolve the cifs name to a posix name */
394         status = pvfs_resolve_name(pvfs, req, info->generic.file.fname, 
395                                    PVFS_RESOLVE_STREAMS, &name);
396         if (!NT_STATUS_IS_OK(status)) {
397                 return status;
398         }
399
400         if (!name->exists) {
401                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
402         }
403
404
405         /* we take a copy of the current file stats, then update
406            newstats in each of the elements below. At the end we
407            compare, and make any changes needed */
408         newstats = *name;
409
410         switch (info->generic.level) {
411         case RAW_SFILEINFO_SETATTR:
412                 if (!null_time(info->setattr.in.write_time)) {
413                         unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
414                 }
415                 if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
416                         newstats.dos.attrib = info->setattr.in.attrib;
417                 }
418                 break;
419
420         case RAW_SFILEINFO_SETATTRE:
421         case RAW_SFILEINFO_STANDARD:
422                 if (!null_time(info->setattre.in.create_time)) {
423                         unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
424                 }
425                 if (!null_time(info->setattre.in.access_time)) {
426                         unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
427                 }
428                 if (!null_time(info->setattre.in.write_time)) {
429                         unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
430                 }
431                 break;
432
433         case RAW_SFILEINFO_EA_SET:
434                 return pvfs_setfileinfo_ea_set(pvfs, name, -1, 
435                                                info->ea_set.in.num_eas,
436                                                info->ea_set.in.eas);
437
438         case RAW_SFILEINFO_BASIC_INFO:
439         case RAW_SFILEINFO_BASIC_INFORMATION:
440                 if (info->basic_info.in.create_time) {
441                         newstats.dos.create_time = info->basic_info.in.create_time;
442                 }
443                 if (info->basic_info.in.access_time) {
444                         newstats.dos.access_time = info->basic_info.in.access_time;
445                 }
446                 if (info->basic_info.in.write_time) {
447                         newstats.dos.write_time = info->basic_info.in.write_time;
448                 }
449                 if (info->basic_info.in.change_time) {
450                         newstats.dos.change_time = info->basic_info.in.change_time;
451                 }
452                 if (info->basic_info.in.attrib != 0) {
453                         newstats.dos.attrib = info->basic_info.in.attrib;
454                 }
455                 break;
456
457         case RAW_SFILEINFO_ALLOCATION_INFO:
458         case RAW_SFILEINFO_ALLOCATION_INFORMATION:
459                 if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
460                         /* strange. Increasing the allocation size via setpathinfo 
461                            should be silently ignored */
462                         break;
463                 }
464                 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
465                 if (newstats.dos.alloc_size < newstats.st.st_size) {
466                         newstats.st.st_size = newstats.dos.alloc_size;
467                 }
468                 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs, 
469                                                                 newstats.dos.alloc_size);
470                 break;
471
472         case RAW_SFILEINFO_END_OF_FILE_INFO:
473         case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
474                 newstats.st.st_size = info->end_of_file_info.in.size;
475                 break;
476
477         case RAW_SFILEINFO_MODE_INFORMATION:
478                 if (info->mode_information.in.mode != 0 &&
479                     info->mode_information.in.mode != 2 &&
480                     info->mode_information.in.mode != 4 &&
481                     info->mode_information.in.mode != 6) {
482                         return NT_STATUS_INVALID_PARAMETER;
483                 }
484                 return NT_STATUS_OK;
485
486         case RAW_SFILEINFO_RENAME_INFORMATION:
487                 return pvfs_setfileinfo_rename(pvfs, req, name, 
488                                                &info->rename_information.in);
489
490         case RAW_SFILEINFO_DISPOSITION_INFO:
491         case RAW_SFILEINFO_DISPOSITION_INFORMATION:
492         case RAW_SFILEINFO_POSITION_INFORMATION:
493                 return NT_STATUS_OK;
494
495         default:
496                 return NT_STATUS_INVALID_LEVEL;
497         }
498
499         /* possibly change the file size */
500         if (newstats.st.st_size != name->st.st_size) {
501                 if (name->stream_name) {
502                         status = pvfs_stream_truncate(pvfs, name, -1, newstats.st.st_size);
503                         if (!NT_STATUS_IS_OK(status)) {
504                                 return status;
505                         }
506                 } else if (truncate(name->full_name, newstats.st.st_size) == -1) {
507                         return pvfs_map_errno(pvfs, errno);
508                 }
509         }
510
511         /* possibly change the file timestamps */
512         ZERO_STRUCT(unix_times);
513         if (newstats.dos.access_time != name->dos.access_time) {
514                 unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
515         }
516         if (newstats.dos.write_time != name->dos.write_time) {
517                 unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
518         }
519         if (unix_times.actime != 0 || unix_times.modtime != 0) {
520                 if (utime(name->full_name, &unix_times) == -1) {
521                         return pvfs_map_errno(pvfs, errno);
522                 }
523         }
524
525         /* possibly change the attribute */
526         newstats.dos.attrib |= (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY);
527         if (newstats.dos.attrib != name->dos.attrib) {
528                 mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
529                 if (chmod(name->full_name, mode) == -1) {
530                         return pvfs_map_errno(pvfs, errno);
531                 }
532         }
533
534         *name = newstats;
535
536         return pvfs_dosattrib_save(pvfs, name, -1);
537 }
538