Merge branch 'v4-0-test' of git://git.samba.org/samba into 4-0-local
[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 3 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, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "vfs_posix.h"
24 #include "system/time.h"
25 #include "librpc/gen_ndr/xattr.h"
26
27
28 /*
29   determine what access bits are needed for a call
30 */
31 static uint32_t pvfs_setfileinfo_access(union smb_setfileinfo *info)
32 {
33         uint32_t needed;
34
35         switch (info->generic.level) {
36         case RAW_SFILEINFO_EA_SET:
37                 needed = SEC_FILE_WRITE_EA;
38                 break;
39
40         case RAW_SFILEINFO_DISPOSITION_INFO:
41         case RAW_SFILEINFO_DISPOSITION_INFORMATION:
42                 needed = SEC_STD_DELETE;
43                 break;
44
45         case RAW_SFILEINFO_END_OF_FILE_INFO:
46                 needed = SEC_FILE_WRITE_DATA;
47                 break;
48
49         case RAW_SFILEINFO_POSITION_INFORMATION:
50                 needed = 0;
51                 break;
52
53         case RAW_SFILEINFO_SEC_DESC:
54                 needed = 0;
55                 if (info->set_secdesc.in.secinfo_flags & (SECINFO_OWNER|SECINFO_GROUP)) {
56                         needed |= SEC_STD_WRITE_OWNER;
57                 }
58                 if (info->set_secdesc.in.secinfo_flags & SECINFO_DACL) {
59                         needed |= SEC_STD_WRITE_DAC;
60                 }
61                 if (info->set_secdesc.in.secinfo_flags & SECINFO_SACL) {
62                         needed |= SEC_FLAG_SYSTEM_SECURITY;
63                 }
64                 break;
65
66         case RAW_SFILEINFO_RENAME_INFORMATION:
67         case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
68                 needed = SEC_STD_DELETE;
69                 break;
70
71         default:
72                 needed = SEC_FILE_WRITE_ATTRIBUTE;
73                 break;
74         }
75
76         return needed;
77 }
78
79 /*
80   rename_information level
81 */
82 static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs, 
83                                         struct ntvfs_request *req, 
84                                         struct pvfs_filename *name,
85                                         union smb_setfileinfo *info)
86 {
87         NTSTATUS status;
88         struct pvfs_filename *name2;
89         char *new_name, *p;
90
91         /* renames are only allowed within a directory */
92         if (strchr_m(info->rename_information.in.new_name, '\\') &&
93             (req->ctx->protocol != PROTOCOL_SMB2)) {
94                 return NT_STATUS_NOT_SUPPORTED;
95         }
96
97         if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
98                 /* don't allow this for now */
99                 return NT_STATUS_FILE_IS_A_DIRECTORY;
100         }
101
102         /* don't allow stream renames for now */
103         if (name->stream_name) {
104                 return NT_STATUS_INVALID_PARAMETER;
105         }
106
107         /* w2k3 does not appear to allow relative rename. On SMB2, vista sends it sometimes,
108            but I suspect it is just uninitialised memory */
109         if (info->rename_information.in.root_fid != 0 && 
110             (req->ctx->protocol != PROTOCOL_SMB2)) {
111                 return NT_STATUS_INVALID_PARAMETER;
112         }
113
114         /* construct the fully qualified windows name for the new file name */
115         if (req->ctx->protocol == PROTOCOL_SMB2) {
116                 /* SMB2 sends the full path of the new name */
117                 new_name = talloc_asprintf(req, "\\%s", info->rename_information.in.new_name);
118         } else {
119                 new_name = talloc_strdup(req, name->original_name);
120                 if (new_name == NULL) {
121                         return NT_STATUS_NO_MEMORY;
122                 }
123                 p = strrchr_m(new_name, '\\');
124                 if (p == NULL) {
125                         return NT_STATUS_OBJECT_NAME_INVALID;
126                 } else {
127                         *p = 0;
128                 }
129
130                 new_name = talloc_asprintf(req, "%s\\%s", new_name,
131                                            info->rename_information.in.new_name);
132         }
133         if (new_name == NULL) {
134                 return NT_STATUS_NO_MEMORY;
135         }
136
137         /* resolve the new name */
138         status = pvfs_resolve_name(pvfs, name, new_name, 0, &name2);
139         if (!NT_STATUS_IS_OK(status)) {
140                 return status;
141         }
142
143         /* if the destination exists, then check the rename is allowed */
144         if (name2->exists) {
145                 if (strcmp(name2->full_name, name->full_name) == 0) {
146                         /* rename to same name is null-op */
147                         return NT_STATUS_OK;
148                 }
149
150                 if (!info->rename_information.in.overwrite) {
151                         return NT_STATUS_OBJECT_NAME_COLLISION;
152                 }
153
154                 status = pvfs_can_delete(pvfs, req, name2, NULL);
155                 if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) {
156                         return NT_STATUS_ACCESS_DENIED;
157                 }
158                 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
159                         return NT_STATUS_ACCESS_DENIED;
160                 }
161                 if (!NT_STATUS_IS_OK(status)) {
162                         return status;
163                 }
164         }
165
166         status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
167         if (!NT_STATUS_IS_OK(status)) {
168                 return status;
169         }
170
171         status = pvfs_do_rename(pvfs, name, name2->full_name);
172         if (NT_STATUS_IS_OK(status)) {
173                 name->full_name = talloc_steal(name, name2->full_name);
174                 name->original_name = talloc_steal(name, name2->original_name);
175         }
176
177         return NT_STATUS_OK;
178 }
179
180 /*
181   add a single DOS EA
182 */
183 NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs, 
184                                  struct pvfs_filename *name,
185                                  int fd, uint16_t num_eas,
186                                  struct ea_struct *eas)
187 {
188         struct xattr_DosEAs *ealist;
189         int i, j;
190         NTSTATUS status;
191
192         if (num_eas == 0) {
193                 return NT_STATUS_OK;
194         }
195
196         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
197                 return NT_STATUS_NOT_SUPPORTED;
198         }
199
200         ealist = talloc(name, struct xattr_DosEAs);
201
202         /* load the current list */
203         status = pvfs_doseas_load(pvfs, name, fd, ealist);
204         if (!NT_STATUS_IS_OK(status)) {
205                 return status;
206         }
207
208         for (j=0;j<num_eas;j++) {
209                 struct ea_struct *ea = &eas[j];
210                 /* see if its already there */
211                 for (i=0;i<ealist->num_eas;i++) {
212                         if (strcasecmp_m(ealist->eas[i].name, ea->name.s) == 0) {
213                                 ealist->eas[i].value = ea->value;
214                                 break;
215                         }
216                 }
217
218                 if (i==ealist->num_eas) {
219                         /* add it */
220                         ealist->eas = talloc_realloc(ealist, ealist->eas, 
221                                                        struct xattr_EA, 
222                                                        ealist->num_eas+1);
223                         if (ealist->eas == NULL) {
224                                 return NT_STATUS_NO_MEMORY;
225                         }
226                         ealist->eas[i].name = ea->name.s;
227                         ealist->eas[i].value = ea->value;
228                         ealist->num_eas++;
229                 }
230         }
231         
232         /* pull out any null EAs */
233         for (i=0;i<ealist->num_eas;i++) {
234                 if (ealist->eas[i].value.length == 0) {
235                         memmove(&ealist->eas[i],
236                                 &ealist->eas[i+1],
237                                 (ealist->num_eas-(i+1)) * sizeof(ealist->eas[i]));
238                         ealist->num_eas--;
239                         i--;
240                 }
241         }
242
243         status = pvfs_doseas_save(pvfs, name, fd, ealist);
244         if (!NT_STATUS_IS_OK(status)) {
245                 return status;
246         }
247
248         notify_trigger(pvfs->notify_context, 
249                        NOTIFY_ACTION_MODIFIED, 
250                        FILE_NOTIFY_CHANGE_EA,
251                        name->full_name);
252
253         name->dos.ea_size = 4;
254         for (i=0;i<ealist->num_eas;i++) {
255                 name->dos.ea_size += 4 + strlen(ealist->eas[i].name)+1 + 
256                         ealist->eas[i].value.length;
257         }
258
259         /* update the ea_size attrib */
260         return pvfs_dosattrib_save(pvfs, name, fd);
261 }
262
263 /*
264   set info on a open file
265 */
266 NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
267                           struct ntvfs_request *req, 
268                           union smb_setfileinfo *info)
269 {
270         struct pvfs_state *pvfs = ntvfs->private_data;
271         struct utimbuf unix_times;
272         struct pvfs_file *f;
273         struct pvfs_file_handle *h;
274         struct pvfs_filename newstats;
275         NTSTATUS status;
276         uint32_t access_needed;
277         uint32_t change_mask = 0;
278
279         f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
280         if (!f) {
281                 return NT_STATUS_INVALID_HANDLE;
282         }
283
284         h = f->handle;
285
286         access_needed = pvfs_setfileinfo_access(info);
287         if ((f->access_mask & access_needed) != access_needed) {
288                 return NT_STATUS_ACCESS_DENIED;
289         }
290
291         /* update the file information */
292         status = pvfs_resolve_name_fd(pvfs, h->fd, h->name);
293         if (!NT_STATUS_IS_OK(status)) {
294                 return status;
295         }
296
297         /* we take a copy of the current file stats, then update
298            newstats in each of the elements below. At the end we
299            compare, and make any changes needed */
300         newstats = *h->name;
301
302         switch (info->generic.level) {
303         case RAW_SFILEINFO_SETATTR:
304                 if (!null_time(info->setattr.in.write_time)) {
305                         unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
306                 }
307                 if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
308                         newstats.dos.attrib = info->setattr.in.attrib;
309                 }
310                 break;
311
312         case RAW_SFILEINFO_SETATTRE:
313         case RAW_SFILEINFO_STANDARD:
314                 if (!null_time(info->setattre.in.create_time)) {
315                         unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
316                 }
317                 if (!null_time(info->setattre.in.access_time)) {
318                         unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
319                 }
320                 if (!null_time(info->setattre.in.write_time)) {
321                         unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
322                 }
323                 break;
324
325         case RAW_SFILEINFO_EA_SET:
326                 return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd, 
327                                                info->ea_set.in.num_eas,
328                                                info->ea_set.in.eas);
329
330         case RAW_SFILEINFO_BASIC_INFO:
331         case RAW_SFILEINFO_BASIC_INFORMATION:
332                 if (!null_nttime(info->basic_info.in.create_time)) {
333                         newstats.dos.create_time = info->basic_info.in.create_time;
334                 }
335                 if (!null_nttime(info->basic_info.in.access_time)) {
336                         newstats.dos.access_time = info->basic_info.in.access_time;
337                 }
338                 if (!null_nttime(info->basic_info.in.write_time)) {
339                         newstats.dos.write_time = info->basic_info.in.write_time;
340                         newstats.dos.flags |= XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
341                         h->sticky_write_time = true;
342                 }
343                 if (!null_nttime(info->basic_info.in.change_time)) {
344                         newstats.dos.change_time = info->basic_info.in.change_time;
345                 }
346                 if (info->basic_info.in.attrib != 0) {
347                         newstats.dos.attrib = info->basic_info.in.attrib;
348                 }
349                 break;
350
351         case RAW_SFILEINFO_DISPOSITION_INFO:
352         case RAW_SFILEINFO_DISPOSITION_INFORMATION:
353                 return pvfs_set_delete_on_close(pvfs, req, f, 
354                                                 info->disposition_info.in.delete_on_close);
355
356         case RAW_SFILEINFO_ALLOCATION_INFO:
357         case RAW_SFILEINFO_ALLOCATION_INFORMATION:
358                 status = pvfs_break_level2_oplocks(f);
359                 NT_STATUS_NOT_OK_RETURN(status);
360
361                 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
362                 if (newstats.dos.alloc_size < newstats.st.st_size) {
363                         newstats.st.st_size = newstats.dos.alloc_size;
364                 }
365                 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs, 
366                                                                 newstats.dos.alloc_size);
367                 break;
368
369         case RAW_SFILEINFO_END_OF_FILE_INFO:
370         case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
371                 status = pvfs_break_level2_oplocks(f);
372                 NT_STATUS_NOT_OK_RETURN(status);
373
374                 newstats.st.st_size = info->end_of_file_info.in.size;
375                 break;
376
377         case RAW_SFILEINFO_POSITION_INFORMATION:
378                 h->position = info->position_information.in.position;
379                 break;
380
381         case RAW_SFILEINFO_MODE_INFORMATION:
382                 /* this one is a puzzle */
383                 if (info->mode_information.in.mode != 0 &&
384                     info->mode_information.in.mode != 2 &&
385                     info->mode_information.in.mode != 4 &&
386                     info->mode_information.in.mode != 6) {
387                         return NT_STATUS_INVALID_PARAMETER;
388                 }
389                 h->mode = info->mode_information.in.mode;
390                 break;
391
392         case RAW_SFILEINFO_RENAME_INFORMATION:
393         case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
394                 return pvfs_setfileinfo_rename(pvfs, req, h->name, 
395                                                info);
396
397         case RAW_SFILEINFO_SEC_DESC:
398                 notify_trigger(pvfs->notify_context, 
399                                NOTIFY_ACTION_MODIFIED, 
400                                FILE_NOTIFY_CHANGE_SECURITY,
401                                h->name->full_name);
402                 return pvfs_acl_set(pvfs, req, h->name, h->fd, f->access_mask, info);
403
404         default:
405                 return NT_STATUS_INVALID_LEVEL;
406         }
407
408         /* possibly change the file size */
409         if (newstats.st.st_size != h->name->st.st_size) {
410                 if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
411                         return NT_STATUS_FILE_IS_A_DIRECTORY;
412                 }
413                 if (h->name->stream_name) {
414                         status = pvfs_stream_truncate(pvfs, h->name, h->fd, newstats.st.st_size);
415                         if (!NT_STATUS_IS_OK(status)) {
416                                 return status;
417                         }
418                         
419                         change_mask |= FILE_NOTIFY_CHANGE_STREAM_SIZE;
420                 } else {
421                         int ret;
422                         if (f->access_mask & 
423                             (SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA)) {
424                                 ret = ftruncate(h->fd, newstats.st.st_size);
425                         } else {
426                                 ret = truncate(h->name->full_name, newstats.st.st_size);
427                         }
428                         if (ret == -1) {
429                                 return pvfs_map_errno(pvfs, errno);
430                         }
431                         change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
432                 }
433         }
434
435         /* possibly change the file timestamps */
436         ZERO_STRUCT(unix_times);
437         if (newstats.dos.create_time != h->name->dos.create_time) {
438                 change_mask |= FILE_NOTIFY_CHANGE_CREATION;
439         }
440         if (newstats.dos.access_time != h->name->dos.access_time) {
441                 unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
442                 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
443         }
444         if (newstats.dos.write_time != h->name->dos.write_time) {
445                 unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
446                 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
447         }
448         if (unix_times.actime != 0 || unix_times.modtime != 0) {
449                 if (utime(h->name->full_name, &unix_times) == -1) {
450                         return pvfs_map_errno(pvfs, errno);
451                 }
452         }
453
454         /* possibly change the attribute */
455         if (newstats.dos.attrib != h->name->dos.attrib) {
456                 mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
457                 if (!(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
458                         if (fchmod(h->fd, mode) == -1) {
459                                 return pvfs_map_errno(pvfs, errno);
460                         }
461                 }
462                 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
463         }
464
465         *h->name = newstats;
466
467         notify_trigger(pvfs->notify_context, 
468                        NOTIFY_ACTION_MODIFIED, 
469                        change_mask,
470                        h->name->full_name);
471
472         return pvfs_dosattrib_save(pvfs, h->name, h->fd);
473 }
474
475 /*
476   retry an open after a sharing violation
477 */
478 static void pvfs_retry_setpathinfo(struct pvfs_odb_retry *r,
479                                    struct ntvfs_module_context *ntvfs,
480                                    struct ntvfs_request *req,
481                                    void *_info,
482                                    void *private_data,
483                                    enum pvfs_wait_notice reason)
484 {
485         union smb_setfileinfo *info = talloc_get_type(_info,
486                                       union smb_setfileinfo);
487         NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
488
489         talloc_free(r);
490
491         switch (reason) {
492         case PVFS_WAIT_CANCEL:
493 /*TODO*/
494                 status = NT_STATUS_CANCELLED;
495                 break;
496         case PVFS_WAIT_TIMEOUT:
497                 /* if it timed out, then give the failure
498                    immediately */
499 /*TODO*/
500                 status = NT_STATUS_SHARING_VIOLATION;
501                 break;
502         case PVFS_WAIT_EVENT:
503
504                 /* try the open again, which could trigger another retry setup
505                    if it wants to, so we have to unmark the async flag so we
506                    will know if it does a second async reply */
507                 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
508
509                 status = pvfs_setpathinfo(ntvfs, req, info);
510                 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
511                         /* the 2nd try also replied async, so we don't send
512                            the reply yet */
513                         return;
514                 }
515
516                 /* re-mark it async, just in case someone up the chain does
517                    paranoid checking */
518                 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
519                 break;
520         }
521
522         /* send the reply up the chain */
523         req->async_states->status = status;
524         req->async_states->send_fn(req);
525 }
526
527 /*
528   setup for a unlink retry after a sharing violation
529   or a non granted oplock
530 */
531 static NTSTATUS pvfs_setpathinfo_setup_retry(struct ntvfs_module_context *ntvfs,
532                                              struct ntvfs_request *req,
533                                              union smb_setfileinfo *info,
534                                              struct odb_lock *lck,
535                                              NTSTATUS status)
536 {
537         struct pvfs_state *pvfs = ntvfs->private_data;
538         struct timeval end_time;
539
540         if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
541                 end_time = timeval_add(&req->statistics.request_time,
542                                        0, pvfs->sharing_violation_delay);
543         } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
544                 end_time = timeval_add(&req->statistics.request_time,
545                                        pvfs->oplock_break_timeout, 0);
546         } else {
547                 return NT_STATUS_INTERNAL_ERROR;
548         }
549
550         return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, info, NULL,
551                                     pvfs_retry_setpathinfo);
552 }
553
554 /*
555   set info on a pathname
556 */
557 NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
558                           struct ntvfs_request *req, union smb_setfileinfo *info)
559 {
560         struct pvfs_state *pvfs = ntvfs->private_data;
561         struct pvfs_filename *name;
562         struct pvfs_filename newstats;
563         NTSTATUS status;
564         struct utimbuf unix_times;
565         uint32_t access_needed;
566         uint32_t change_mask = 0;
567         struct odb_lock *lck = NULL;
568
569         /* resolve the cifs name to a posix name */
570         status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path, 
571                                    PVFS_RESOLVE_STREAMS, &name);
572         if (!NT_STATUS_IS_OK(status)) {
573                 return status;
574         }
575
576         if (!name->exists) {
577                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
578         }
579
580         access_needed = pvfs_setfileinfo_access(info);
581         status = pvfs_access_check_simple(pvfs, req, name, access_needed);
582         if (!NT_STATUS_IS_OK(status)) {
583                 return status;
584         }
585
586         /* we take a copy of the current file stats, then update
587            newstats in each of the elements below. At the end we
588            compare, and make any changes needed */
589         newstats = *name;
590
591         switch (info->generic.level) {
592         case RAW_SFILEINFO_SETATTR:
593                 if (!null_time(info->setattr.in.write_time)) {
594                         unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
595                 }
596                 if (info->setattr.in.attrib == 0) {
597                         newstats.dos.attrib = FILE_ATTRIBUTE_NORMAL;
598                 } else if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
599                         newstats.dos.attrib = info->setattr.in.attrib;
600                 }
601                 break;
602
603         case RAW_SFILEINFO_SETATTRE:
604         case RAW_SFILEINFO_STANDARD:
605                 if (!null_time(info->setattre.in.create_time)) {
606                         unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
607                 }
608                 if (!null_time(info->setattre.in.access_time)) {
609                         unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
610                 }
611                 if (!null_time(info->setattre.in.write_time)) {
612                         unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
613                 }
614                 break;
615
616         case RAW_SFILEINFO_EA_SET:
617                 return pvfs_setfileinfo_ea_set(pvfs, name, -1, 
618                                                info->ea_set.in.num_eas,
619                                                info->ea_set.in.eas);
620
621         case RAW_SFILEINFO_BASIC_INFO:
622         case RAW_SFILEINFO_BASIC_INFORMATION:
623                 if (!null_nttime(info->basic_info.in.create_time)) {
624                         newstats.dos.create_time = info->basic_info.in.create_time;
625                 }
626                 if (!null_nttime(info->basic_info.in.access_time)) {
627                         newstats.dos.access_time = info->basic_info.in.access_time;
628                 }
629                 if (!null_nttime(info->basic_info.in.write_time)) {
630                         newstats.dos.write_time = info->basic_info.in.write_time;
631                 }
632                 if (!null_nttime(info->basic_info.in.change_time)) {
633                         newstats.dos.change_time = info->basic_info.in.change_time;
634                 }
635                 if (info->basic_info.in.attrib != 0) {
636                         newstats.dos.attrib = info->basic_info.in.attrib;
637                 }
638                 break;
639
640         case RAW_SFILEINFO_ALLOCATION_INFO:
641         case RAW_SFILEINFO_ALLOCATION_INFORMATION:
642                 status = pvfs_can_update_file_size(pvfs, req, name, &lck);
643                 /*
644                  * on a sharing violation we need to retry when the file is closed by
645                  * the other user, or after 1 second
646                  * on a non granted oplock we need to retry when the file is closed by
647                  * the other user, or after 30 seconds
648                 */
649                 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
650                      NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
651                     (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
652                         return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
653                 }
654                 NT_STATUS_NOT_OK_RETURN(status);
655
656                 if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
657                         /* strange. Increasing the allocation size via setpathinfo 
658                            should be silently ignored */
659                         break;
660                 }
661                 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
662                 if (newstats.dos.alloc_size < newstats.st.st_size) {
663                         newstats.st.st_size = newstats.dos.alloc_size;
664                 }
665                 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs, 
666                                                                 newstats.dos.alloc_size);
667                 break;
668
669         case RAW_SFILEINFO_END_OF_FILE_INFO:
670         case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
671                 status = pvfs_can_update_file_size(pvfs, req, name, &lck);
672                 /*
673                  * on a sharing violation we need to retry when the file is closed by
674                  * the other user, or after 1 second
675                  * on a non granted oplock we need to retry when the file is closed by
676                  * the other user, or after 30 seconds
677                 */
678                 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
679                      NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
680                     (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
681                         return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
682                 }
683                 NT_STATUS_NOT_OK_RETURN(status);
684
685                 newstats.st.st_size = info->end_of_file_info.in.size;
686                 break;
687
688         case RAW_SFILEINFO_MODE_INFORMATION:
689                 if (info->mode_information.in.mode != 0 &&
690                     info->mode_information.in.mode != 2 &&
691                     info->mode_information.in.mode != 4 &&
692                     info->mode_information.in.mode != 6) {
693                         return NT_STATUS_INVALID_PARAMETER;
694                 }
695                 return NT_STATUS_OK;
696
697         case RAW_SFILEINFO_RENAME_INFORMATION:
698         case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
699                 return pvfs_setfileinfo_rename(pvfs, req, name, 
700                                                info);
701
702         case RAW_SFILEINFO_DISPOSITION_INFO:
703         case RAW_SFILEINFO_DISPOSITION_INFORMATION:
704         case RAW_SFILEINFO_POSITION_INFORMATION:
705                 return NT_STATUS_OK;
706
707         default:
708                 return NT_STATUS_INVALID_LEVEL;
709         }
710
711         /* possibly change the file size */
712         if (newstats.st.st_size != name->st.st_size) {
713                 if (name->stream_name) {
714                         status = pvfs_stream_truncate(pvfs, name, -1, newstats.st.st_size);
715                         if (!NT_STATUS_IS_OK(status)) {
716                                 return status;
717                         }
718                 } else if (truncate(name->full_name, newstats.st.st_size) == -1) {
719                         return pvfs_map_errno(pvfs, errno);
720                 }
721                 change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
722         }
723
724         /* possibly change the file timestamps */
725         ZERO_STRUCT(unix_times);
726         if (newstats.dos.create_time != name->dos.create_time) {
727                 change_mask |= FILE_NOTIFY_CHANGE_CREATION;
728         }
729         if (newstats.dos.access_time != name->dos.access_time) {
730                 unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
731                 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
732         }
733         if (newstats.dos.write_time != name->dos.write_time) {
734                 unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
735                 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
736         }
737         if (unix_times.actime != 0 || unix_times.modtime != 0) {
738                 if (utime(name->full_name, &unix_times) == -1) {
739                         return pvfs_map_errno(pvfs, errno);
740                 }
741         }
742
743         /* possibly change the attribute */
744         newstats.dos.attrib |= (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY);
745         if (newstats.dos.attrib != name->dos.attrib) {
746                 mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
747                 if (chmod(name->full_name, mode) == -1) {
748                         return pvfs_map_errno(pvfs, errno);
749                 }
750                 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
751         }
752
753         *name = newstats;
754
755         if (change_mask != 0) {
756                 notify_trigger(pvfs->notify_context, 
757                                NOTIFY_ACTION_MODIFIED, 
758                                change_mask,
759                                name->full_name);
760         }
761
762         return pvfs_dosattrib_save(pvfs, name, -1);
763 }
764