Fix up the libnet tests.
[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_SHARING_VIOLATION)) {
156                         return NT_STATUS_ACCESS_DENIED;
157                 }
158                 if (!NT_STATUS_IS_OK(status)) {
159                         return status;
160                 }
161         }
162
163         status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
164         if (!NT_STATUS_IS_OK(status)) {
165                 return status;
166         }
167
168         status = pvfs_do_rename(pvfs, name, name2->full_name);
169         if (NT_STATUS_IS_OK(status)) {
170                 name->full_name = talloc_steal(name, name2->full_name);
171                 name->original_name = talloc_steal(name, name2->original_name);
172         }
173
174         return NT_STATUS_OK;
175 }
176
177 /*
178   add a single DOS EA
179 */
180 NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs, 
181                                  struct pvfs_filename *name,
182                                  int fd, uint16_t num_eas,
183                                  struct ea_struct *eas)
184 {
185         struct xattr_DosEAs *ealist;
186         int i, j;
187         NTSTATUS status;
188
189         if (num_eas == 0) {
190                 return NT_STATUS_OK;
191         }
192
193         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
194                 return NT_STATUS_NOT_SUPPORTED;
195         }
196
197         ealist = talloc(name, struct xattr_DosEAs);
198
199         /* load the current list */
200         status = pvfs_doseas_load(pvfs, name, fd, ealist);
201         if (!NT_STATUS_IS_OK(status)) {
202                 return status;
203         }
204
205         for (j=0;j<num_eas;j++) {
206                 struct ea_struct *ea = &eas[j];
207                 /* see if its already there */
208                 for (i=0;i<ealist->num_eas;i++) {
209                         if (strcasecmp_m(ealist->eas[i].name, ea->name.s) == 0) {
210                                 ealist->eas[i].value = ea->value;
211                                 break;
212                         }
213                 }
214
215                 if (i==ealist->num_eas) {
216                         /* add it */
217                         ealist->eas = talloc_realloc(ealist, ealist->eas, 
218                                                        struct xattr_EA, 
219                                                        ealist->num_eas+1);
220                         if (ealist->eas == NULL) {
221                                 return NT_STATUS_NO_MEMORY;
222                         }
223                         ealist->eas[i].name = ea->name.s;
224                         ealist->eas[i].value = ea->value;
225                         ealist->num_eas++;
226                 }
227         }
228         
229         /* pull out any null EAs */
230         for (i=0;i<ealist->num_eas;i++) {
231                 if (ealist->eas[i].value.length == 0) {
232                         memmove(&ealist->eas[i],
233                                 &ealist->eas[i+1],
234                                 (ealist->num_eas-(i+1)) * sizeof(ealist->eas[i]));
235                         ealist->num_eas--;
236                         i--;
237                 }
238         }
239
240         status = pvfs_doseas_save(pvfs, name, fd, ealist);
241         if (!NT_STATUS_IS_OK(status)) {
242                 return status;
243         }
244
245         notify_trigger(pvfs->notify_context, 
246                        NOTIFY_ACTION_MODIFIED, 
247                        FILE_NOTIFY_CHANGE_EA,
248                        name->full_name);
249
250         name->dos.ea_size = 4;
251         for (i=0;i<ealist->num_eas;i++) {
252                 name->dos.ea_size += 4 + strlen(ealist->eas[i].name)+1 + 
253                         ealist->eas[i].value.length;
254         }
255
256         /* update the ea_size attrib */
257         return pvfs_dosattrib_save(pvfs, name, fd);
258 }
259
260 /*
261   set info on a open file
262 */
263 NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
264                           struct ntvfs_request *req, 
265                           union smb_setfileinfo *info)
266 {
267         struct pvfs_state *pvfs = ntvfs->private_data;
268         struct utimbuf unix_times;
269         struct pvfs_file *f;
270         struct pvfs_file_handle *h;
271         struct pvfs_filename newstats;
272         NTSTATUS status;
273         uint32_t access_needed;
274         uint32_t change_mask = 0;
275
276         f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
277         if (!f) {
278                 return NT_STATUS_INVALID_HANDLE;
279         }
280
281         h = f->handle;
282
283         access_needed = pvfs_setfileinfo_access(info);
284         if ((f->access_mask & access_needed) != access_needed) {
285                 return NT_STATUS_ACCESS_DENIED;
286         }
287
288         /* update the file information */
289         status = pvfs_resolve_name_fd(pvfs, h->fd, h->name);
290         if (!NT_STATUS_IS_OK(status)) {
291                 return status;
292         }
293
294         /* we take a copy of the current file stats, then update
295            newstats in each of the elements below. At the end we
296            compare, and make any changes needed */
297         newstats = *h->name;
298
299         switch (info->generic.level) {
300         case RAW_SFILEINFO_SETATTR:
301                 if (!null_time(info->setattr.in.write_time)) {
302                         unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
303                 }
304                 if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
305                         newstats.dos.attrib = info->setattr.in.attrib;
306                 }
307                 break;
308
309         case RAW_SFILEINFO_SETATTRE:
310         case RAW_SFILEINFO_STANDARD:
311                 if (!null_time(info->setattre.in.create_time)) {
312                         unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
313                 }
314                 if (!null_time(info->setattre.in.access_time)) {
315                         unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
316                 }
317                 if (!null_time(info->setattre.in.write_time)) {
318                         unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
319                 }
320                 break;
321
322         case RAW_SFILEINFO_EA_SET:
323                 return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd, 
324                                                info->ea_set.in.num_eas,
325                                                info->ea_set.in.eas);
326
327         case RAW_SFILEINFO_BASIC_INFO:
328         case RAW_SFILEINFO_BASIC_INFORMATION:
329                 if (!null_nttime(info->basic_info.in.create_time)) {
330                         newstats.dos.create_time = info->basic_info.in.create_time;
331                 }
332                 if (!null_nttime(info->basic_info.in.access_time)) {
333                         newstats.dos.access_time = info->basic_info.in.access_time;
334                 }
335                 if (!null_nttime(info->basic_info.in.write_time)) {
336                         newstats.dos.write_time = info->basic_info.in.write_time;
337                         newstats.dos.flags |= XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
338                         h->sticky_write_time = true;
339                 }
340                 if (!null_nttime(info->basic_info.in.change_time)) {
341                         newstats.dos.change_time = info->basic_info.in.change_time;
342                 }
343                 if (info->basic_info.in.attrib != 0) {
344                         newstats.dos.attrib = info->basic_info.in.attrib;
345                 }
346                 break;
347
348         case RAW_SFILEINFO_DISPOSITION_INFO:
349         case RAW_SFILEINFO_DISPOSITION_INFORMATION:
350                 return pvfs_set_delete_on_close(pvfs, req, f, 
351                                                 info->disposition_info.in.delete_on_close);
352
353         case RAW_SFILEINFO_ALLOCATION_INFO:
354         case RAW_SFILEINFO_ALLOCATION_INFORMATION:
355                 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
356                 if (newstats.dos.alloc_size < newstats.st.st_size) {
357                         newstats.st.st_size = newstats.dos.alloc_size;
358                 }
359                 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs, 
360                                                                 newstats.dos.alloc_size);
361                 break;
362
363         case RAW_SFILEINFO_END_OF_FILE_INFO:
364         case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
365                 newstats.st.st_size = info->end_of_file_info.in.size;
366                 break;
367
368         case RAW_SFILEINFO_POSITION_INFORMATION:
369                 h->position = info->position_information.in.position;
370                 break;
371
372         case RAW_SFILEINFO_MODE_INFORMATION:
373                 /* this one is a puzzle */
374                 if (info->mode_information.in.mode != 0 &&
375                     info->mode_information.in.mode != 2 &&
376                     info->mode_information.in.mode != 4 &&
377                     info->mode_information.in.mode != 6) {
378                         return NT_STATUS_INVALID_PARAMETER;
379                 }
380                 h->mode = info->mode_information.in.mode;
381                 break;
382
383         case RAW_SFILEINFO_RENAME_INFORMATION:
384         case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
385                 return pvfs_setfileinfo_rename(pvfs, req, h->name, 
386                                                info);
387
388         case RAW_SFILEINFO_SEC_DESC:
389                 notify_trigger(pvfs->notify_context, 
390                                NOTIFY_ACTION_MODIFIED, 
391                                FILE_NOTIFY_CHANGE_SECURITY,
392                                h->name->full_name);
393                 return pvfs_acl_set(pvfs, req, h->name, h->fd, f->access_mask, info);
394
395         default:
396                 return NT_STATUS_INVALID_LEVEL;
397         }
398
399         /* possibly change the file size */
400         if (newstats.st.st_size != h->name->st.st_size) {
401                 if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
402                         return NT_STATUS_FILE_IS_A_DIRECTORY;
403                 }
404                 if (h->name->stream_name) {
405                         status = pvfs_stream_truncate(pvfs, h->name, h->fd, newstats.st.st_size);
406                         if (!NT_STATUS_IS_OK(status)) {
407                                 return status;
408                         }
409                         
410                         change_mask |= FILE_NOTIFY_CHANGE_STREAM_SIZE;
411                 } else {
412                         int ret;
413                         if (f->access_mask & 
414                             (SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA)) {
415                                 ret = ftruncate(h->fd, newstats.st.st_size);
416                         } else {
417                                 ret = truncate(h->name->full_name, newstats.st.st_size);
418                         }
419                         if (ret == -1) {
420                                 return pvfs_map_errno(pvfs, errno);
421                         }
422                         change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
423                 }
424         }
425
426         /* possibly change the file timestamps */
427         ZERO_STRUCT(unix_times);
428         if (newstats.dos.create_time != h->name->dos.create_time) {
429                 change_mask |= FILE_NOTIFY_CHANGE_CREATION;
430         }
431         if (newstats.dos.access_time != h->name->dos.access_time) {
432                 unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
433                 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
434         }
435         if (newstats.dos.write_time != h->name->dos.write_time) {
436                 unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
437                 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
438         }
439         if (unix_times.actime != 0 || unix_times.modtime != 0) {
440                 if (utime(h->name->full_name, &unix_times) == -1) {
441                         return pvfs_map_errno(pvfs, errno);
442                 }
443         }
444
445         /* possibly change the attribute */
446         if (newstats.dos.attrib != h->name->dos.attrib) {
447                 mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
448                 if (!(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
449                         if (fchmod(h->fd, mode) == -1) {
450                                 return pvfs_map_errno(pvfs, errno);
451                         }
452                 }
453                 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
454         }
455
456         *h->name = newstats;
457
458         notify_trigger(pvfs->notify_context, 
459                        NOTIFY_ACTION_MODIFIED, 
460                        change_mask,
461                        h->name->full_name);
462
463         return pvfs_dosattrib_save(pvfs, h->name, h->fd);
464 }
465
466
467 /*
468   set info on a pathname
469 */
470 NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
471                           struct ntvfs_request *req, union smb_setfileinfo *info)
472 {
473         struct pvfs_state *pvfs = ntvfs->private_data;
474         struct pvfs_filename *name;
475         struct pvfs_filename newstats;
476         NTSTATUS status;
477         struct utimbuf unix_times;
478         uint32_t access_needed;
479         uint32_t change_mask = 0;
480
481         /* resolve the cifs name to a posix name */
482         status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path, 
483                                    PVFS_RESOLVE_STREAMS, &name);
484         if (!NT_STATUS_IS_OK(status)) {
485                 return status;
486         }
487
488         if (!name->exists) {
489                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
490         }
491
492         access_needed = pvfs_setfileinfo_access(info);
493         status = pvfs_access_check_simple(pvfs, req, name, access_needed);
494         if (!NT_STATUS_IS_OK(status)) {
495                 return status;
496         }
497
498         /* we take a copy of the current file stats, then update
499            newstats in each of the elements below. At the end we
500            compare, and make any changes needed */
501         newstats = *name;
502
503         switch (info->generic.level) {
504         case RAW_SFILEINFO_SETATTR:
505                 if (!null_time(info->setattr.in.write_time)) {
506                         unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
507                 }
508                 if (info->setattr.in.attrib == 0) {
509                         newstats.dos.attrib = FILE_ATTRIBUTE_NORMAL;
510                 } else if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
511                         newstats.dos.attrib = info->setattr.in.attrib;
512                 }
513                 break;
514
515         case RAW_SFILEINFO_SETATTRE:
516         case RAW_SFILEINFO_STANDARD:
517                 if (!null_time(info->setattre.in.create_time)) {
518                         unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
519                 }
520                 if (!null_time(info->setattre.in.access_time)) {
521                         unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
522                 }
523                 if (!null_time(info->setattre.in.write_time)) {
524                         unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
525                 }
526                 break;
527
528         case RAW_SFILEINFO_EA_SET:
529                 return pvfs_setfileinfo_ea_set(pvfs, name, -1, 
530                                                info->ea_set.in.num_eas,
531                                                info->ea_set.in.eas);
532
533         case RAW_SFILEINFO_BASIC_INFO:
534         case RAW_SFILEINFO_BASIC_INFORMATION:
535                 if (!null_nttime(info->basic_info.in.create_time)) {
536                         newstats.dos.create_time = info->basic_info.in.create_time;
537                 }
538                 if (!null_nttime(info->basic_info.in.access_time)) {
539                         newstats.dos.access_time = info->basic_info.in.access_time;
540                 }
541                 if (!null_nttime(info->basic_info.in.write_time)) {
542                         newstats.dos.write_time = info->basic_info.in.write_time;
543                 }
544                 if (!null_nttime(info->basic_info.in.change_time)) {
545                         newstats.dos.change_time = info->basic_info.in.change_time;
546                 }
547                 if (info->basic_info.in.attrib != 0) {
548                         newstats.dos.attrib = info->basic_info.in.attrib;
549                 }
550                 break;
551
552         case RAW_SFILEINFO_ALLOCATION_INFO:
553         case RAW_SFILEINFO_ALLOCATION_INFORMATION:
554                 if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
555                         /* strange. Increasing the allocation size via setpathinfo 
556                            should be silently ignored */
557                         break;
558                 }
559                 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
560                 if (newstats.dos.alloc_size < newstats.st.st_size) {
561                         newstats.st.st_size = newstats.dos.alloc_size;
562                 }
563                 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs, 
564                                                                 newstats.dos.alloc_size);
565                 break;
566
567         case RAW_SFILEINFO_END_OF_FILE_INFO:
568         case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
569                 newstats.st.st_size = info->end_of_file_info.in.size;
570                 break;
571
572         case RAW_SFILEINFO_MODE_INFORMATION:
573                 if (info->mode_information.in.mode != 0 &&
574                     info->mode_information.in.mode != 2 &&
575                     info->mode_information.in.mode != 4 &&
576                     info->mode_information.in.mode != 6) {
577                         return NT_STATUS_INVALID_PARAMETER;
578                 }
579                 return NT_STATUS_OK;
580
581         case RAW_SFILEINFO_RENAME_INFORMATION:
582         case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
583                 return pvfs_setfileinfo_rename(pvfs, req, name, 
584                                                info);
585
586         case RAW_SFILEINFO_DISPOSITION_INFO:
587         case RAW_SFILEINFO_DISPOSITION_INFORMATION:
588         case RAW_SFILEINFO_POSITION_INFORMATION:
589                 return NT_STATUS_OK;
590
591         default:
592                 return NT_STATUS_INVALID_LEVEL;
593         }
594
595         /* possibly change the file size */
596         if (newstats.st.st_size != name->st.st_size) {
597                 if (name->stream_name) {
598                         status = pvfs_stream_truncate(pvfs, name, -1, newstats.st.st_size);
599                         if (!NT_STATUS_IS_OK(status)) {
600                                 return status;
601                         }
602                 } else if (truncate(name->full_name, newstats.st.st_size) == -1) {
603                         return pvfs_map_errno(pvfs, errno);
604                 }
605                 change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
606         }
607
608         /* possibly change the file timestamps */
609         ZERO_STRUCT(unix_times);
610         if (newstats.dos.create_time != name->dos.create_time) {
611                 change_mask |= FILE_NOTIFY_CHANGE_CREATION;
612         }
613         if (newstats.dos.access_time != name->dos.access_time) {
614                 unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
615                 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
616         }
617         if (newstats.dos.write_time != name->dos.write_time) {
618                 unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
619                 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
620         }
621         if (unix_times.actime != 0 || unix_times.modtime != 0) {
622                 if (utime(name->full_name, &unix_times) == -1) {
623                         return pvfs_map_errno(pvfs, errno);
624                 }
625         }
626
627         /* possibly change the attribute */
628         newstats.dos.attrib |= (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY);
629         if (newstats.dos.attrib != name->dos.attrib) {
630                 mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
631                 if (chmod(name->full_name, mode) == -1) {
632                         return pvfs_map_errno(pvfs, errno);
633                 }
634                 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
635         }
636
637         *name = newstats;
638
639         if (change_mask != 0) {
640                 notify_trigger(pvfs->notify_context, 
641                                NOTIFY_ACTION_MODIFIED, 
642                                change_mask,
643                                name->full_name);
644         }
645
646         return pvfs_dosattrib_save(pvfs, name, -1);
647 }
648