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