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