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