smbd: Remove superfluous ()
[Samba.git] / source4 / ntvfs / posix / pvfs_setfileinfo.c
blob7fd4e35d4f5606b490e6e1c1ed69bac4eb3a49d2
1 /*
2 Unix SMB/CIFS implementation.
4 POSIX NTVFS backend - setfileinfo
6 Copyright (C) Andrew Tridgell 2004
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.
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.
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/>.
22 #include "includes.h"
23 #include "vfs_posix.h"
24 #include "system/time.h"
25 #include "librpc/gen_ndr/xattr.h"
29 determine what access bits are needed for a call
31 static uint32_t pvfs_setfileinfo_access(union smb_setfileinfo *info)
33 uint32_t needed;
35 switch (info->generic.level) {
36 case RAW_SFILEINFO_EA_SET:
37 needed = SEC_FILE_WRITE_EA;
38 break;
40 case RAW_SFILEINFO_DISPOSITION_INFO:
41 case RAW_SFILEINFO_DISPOSITION_INFORMATION:
42 needed = SEC_STD_DELETE;
43 break;
45 case RAW_SFILEINFO_END_OF_FILE_INFO:
46 needed = SEC_FILE_WRITE_DATA;
47 break;
49 case RAW_SFILEINFO_POSITION_INFORMATION:
50 needed = 0;
51 break;
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;
58 if (info->set_secdesc.in.secinfo_flags & SECINFO_DACL) {
59 needed |= SEC_STD_WRITE_DAC;
61 if (info->set_secdesc.in.secinfo_flags & SECINFO_SACL) {
62 needed |= SEC_FLAG_SYSTEM_SECURITY;
64 break;
66 case RAW_SFILEINFO_RENAME_INFORMATION:
67 case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
68 needed = SEC_STD_DELETE;
69 break;
71 default:
72 needed = SEC_FILE_WRITE_ATTRIBUTE;
73 break;
76 return needed;
80 rename_information level for streams
82 static 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)
89 NTSTATUS status;
90 struct odb_lock *lck = NULL;
92 /* strangely, this gives a sharing violation, not invalid
93 parameter */
94 if (info->rename_information.in.new_name[0] != ':') {
95 return NT_STATUS_SHARING_VIOLATION;
98 status = pvfs_access_check_simple(pvfs, req, name, SEC_FILE_WRITE_ATTRIBUTE);
99 if (!NT_STATUS_IS_OK(status)) {
100 return status;
103 lck = odb_lock(req, pvfs->odb_context, odb_locking_key);
104 if (lck == NULL) {
105 DEBUG(0,("Unable to lock opendb for can_stat\n"));
106 return NT_STATUS_INTERNAL_DB_CORRUPTION;
110 status = pvfs_stream_rename(pvfs, name, fd,
111 info->rename_information.in.new_name+1,
112 info->rename_information.in.overwrite);
113 return status;
117 rename_information level
119 static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs,
120 struct ntvfs_request *req,
121 struct pvfs_filename *name,
122 int fd,
123 DATA_BLOB *odb_locking_key,
124 union smb_setfileinfo *info)
126 NTSTATUS status;
127 struct pvfs_filename *name2;
128 char *new_name, *p;
129 struct odb_lock *lck = NULL;
131 /* renames are only allowed within a directory */
132 if (strchr_m(info->rename_information.in.new_name, '\\') &&
133 (req->ctx->protocol < PROTOCOL_SMB2_02)) {
134 return NT_STATUS_NOT_SUPPORTED;
137 /* handle stream renames specially */
138 if (name->stream_name) {
139 return pvfs_setfileinfo_rename_stream(pvfs, req, name, fd,
140 odb_locking_key, info);
143 /* w2k3 does not appear to allow relative rename. On SMB2, vista sends it sometimes,
144 but I suspect it is just uninitialised memory */
145 if (info->rename_information.in.root_fid != 0 &&
146 (req->ctx->protocol < PROTOCOL_SMB2_02)) {
147 return NT_STATUS_INVALID_PARAMETER;
150 /* construct the fully qualified windows name for the new file name */
151 if (req->ctx->protocol >= PROTOCOL_SMB2_02) {
152 /* SMB2 sends the full path of the new name */
153 new_name = talloc_asprintf(req, "\\%s", info->rename_information.in.new_name);
154 } else {
155 new_name = talloc_strdup(req, name->original_name);
156 if (new_name == NULL) {
157 return NT_STATUS_NO_MEMORY;
159 p = strrchr_m(new_name, '\\');
160 if (p == NULL) {
161 return NT_STATUS_OBJECT_NAME_INVALID;
162 } else {
163 *p = 0;
166 new_name = talloc_asprintf(req, "%s\\%s", new_name,
167 info->rename_information.in.new_name);
169 if (new_name == NULL) {
170 return NT_STATUS_NO_MEMORY;
173 /* resolve the new name */
174 status = pvfs_resolve_name(pvfs, req, new_name, 0, &name2);
175 if (!NT_STATUS_IS_OK(status)) {
176 return status;
179 /* if the destination exists, then check the rename is allowed */
180 if (name2->exists) {
181 if (strcmp(name2->full_name, name->full_name) == 0) {
182 /* rename to same name is null-op */
183 return NT_STATUS_OK;
186 if (!info->rename_information.in.overwrite) {
187 return NT_STATUS_OBJECT_NAME_COLLISION;
190 status = pvfs_can_delete(pvfs, req, name2, NULL);
191 if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) {
192 return NT_STATUS_ACCESS_DENIED;
194 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
195 return NT_STATUS_ACCESS_DENIED;
197 if (!NT_STATUS_IS_OK(status)) {
198 return status;
202 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
203 if (!NT_STATUS_IS_OK(status)) {
204 return status;
207 lck = odb_lock(req, pvfs->odb_context, odb_locking_key);
208 if (lck == NULL) {
209 DEBUG(0,("Unable to lock opendb for can_stat\n"));
210 return NT_STATUS_INTERNAL_DB_CORRUPTION;
213 status = pvfs_do_rename(pvfs, lck, name, name2->full_name);
214 talloc_free(lck);
215 NT_STATUS_NOT_OK_RETURN(status);
216 if (NT_STATUS_IS_OK(status)) {
217 name->full_name = talloc_steal(name, name2->full_name);
218 name->original_name = talloc_steal(name, name2->original_name);
221 return NT_STATUS_OK;
225 add a single DOS EA
227 NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs,
228 struct pvfs_filename *name,
229 int fd, uint16_t num_eas,
230 struct ea_struct *eas)
232 struct xattr_DosEAs *ealist;
233 int i, j;
234 NTSTATUS status;
236 if (num_eas == 0) {
237 return NT_STATUS_OK;
240 if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
241 return NT_STATUS_NOT_SUPPORTED;
244 ealist = talloc(name, struct xattr_DosEAs);
246 /* load the current list */
247 status = pvfs_doseas_load(pvfs, name, fd, ealist);
248 if (!NT_STATUS_IS_OK(status)) {
249 return status;
252 for (j=0;j<num_eas;j++) {
253 struct ea_struct *ea = &eas[j];
254 /* see if its already there */
255 for (i=0;i<ealist->num_eas;i++) {
256 if (strcasecmp_m(ealist->eas[i].name, ea->name.s) == 0) {
257 ealist->eas[i].value = ea->value;
258 break;
262 if (i==ealist->num_eas) {
263 /* add it */
264 ealist->eas = talloc_realloc(ealist, ealist->eas,
265 struct xattr_EA,
266 ealist->num_eas+1);
267 if (ealist->eas == NULL) {
268 return NT_STATUS_NO_MEMORY;
270 ealist->eas[i].name = ea->name.s;
271 ealist->eas[i].value = ea->value;
272 ealist->num_eas++;
276 /* pull out any null EAs */
277 for (i=0;i<ealist->num_eas;i++) {
278 if (ealist->eas[i].value.length == 0) {
279 memmove(&ealist->eas[i],
280 &ealist->eas[i+1],
281 (ealist->num_eas-(i+1)) * sizeof(ealist->eas[i]));
282 ealist->num_eas--;
283 i--;
287 status = pvfs_doseas_save(pvfs, name, fd, ealist);
288 if (!NT_STATUS_IS_OK(status)) {
289 return status;
292 notify_trigger(pvfs->notify_context,
293 NOTIFY_ACTION_MODIFIED,
294 FILE_NOTIFY_CHANGE_EA,
295 name->full_name);
297 name->dos.ea_size = 4;
298 for (i=0;i<ealist->num_eas;i++) {
299 name->dos.ea_size += 4 + strlen(ealist->eas[i].name)+1 +
300 ealist->eas[i].value.length;
303 /* update the ea_size attrib */
304 return pvfs_dosattrib_save(pvfs, name, fd);
308 set info on a open file
310 NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
311 struct ntvfs_request *req,
312 union smb_setfileinfo *info)
314 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
315 struct pvfs_state);
316 struct pvfs_file *f;
317 struct pvfs_file_handle *h;
318 struct pvfs_filename newstats;
319 NTSTATUS status;
320 uint32_t access_needed;
321 uint32_t change_mask = 0;
323 f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
324 if (!f) {
325 return NT_STATUS_INVALID_HANDLE;
328 h = f->handle;
330 access_needed = pvfs_setfileinfo_access(info);
331 if ((f->access_mask & access_needed) != access_needed) {
332 return NT_STATUS_ACCESS_DENIED;
335 /* update the file information */
336 status = pvfs_resolve_name_handle(pvfs, h);
337 if (!NT_STATUS_IS_OK(status)) {
338 return status;
341 /* we take a copy of the current file stats, then update
342 newstats in each of the elements below. At the end we
343 compare, and make any changes needed */
344 newstats = *h->name;
346 switch (info->generic.level) {
347 case RAW_SFILEINFO_SETATTR:
348 if (!null_time(info->setattr.in.write_time)) {
349 unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
351 if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
352 newstats.dos.attrib = info->setattr.in.attrib;
354 break;
356 case RAW_SFILEINFO_SETATTRE:
357 case RAW_SFILEINFO_STANDARD:
358 if (!null_time(info->setattre.in.create_time)) {
359 unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
361 if (!null_time(info->setattre.in.access_time)) {
362 unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
364 if (!null_time(info->setattre.in.write_time)) {
365 unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
367 break;
369 case RAW_SFILEINFO_EA_SET:
370 return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd,
371 info->ea_set.in.num_eas,
372 info->ea_set.in.eas);
374 case RAW_SFILEINFO_BASIC_INFO:
375 case RAW_SFILEINFO_BASIC_INFORMATION:
376 if (!null_nttime(info->basic_info.in.create_time)) {
377 newstats.dos.create_time = info->basic_info.in.create_time;
379 if (!null_nttime(info->basic_info.in.access_time)) {
380 newstats.dos.access_time = info->basic_info.in.access_time;
382 if (!null_nttime(info->basic_info.in.write_time)) {
383 newstats.dos.write_time = info->basic_info.in.write_time;
385 if (!null_nttime(info->basic_info.in.change_time)) {
386 newstats.dos.change_time = info->basic_info.in.change_time;
388 if (info->basic_info.in.attrib != 0) {
389 newstats.dos.attrib = info->basic_info.in.attrib;
391 break;
393 case RAW_SFILEINFO_DISPOSITION_INFO:
394 case RAW_SFILEINFO_DISPOSITION_INFORMATION:
395 return pvfs_set_delete_on_close(pvfs, req, f,
396 info->disposition_info.in.delete_on_close);
398 case RAW_SFILEINFO_ALLOCATION_INFO:
399 case RAW_SFILEINFO_ALLOCATION_INFORMATION:
400 status = pvfs_break_level2_oplocks(f);
401 NT_STATUS_NOT_OK_RETURN(status);
403 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
404 if (newstats.dos.alloc_size < newstats.st.st_size) {
405 newstats.st.st_size = newstats.dos.alloc_size;
407 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs,
408 newstats.dos.alloc_size);
409 break;
411 case RAW_SFILEINFO_END_OF_FILE_INFO:
412 case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
413 status = pvfs_break_level2_oplocks(f);
414 NT_STATUS_NOT_OK_RETURN(status);
416 newstats.st.st_size = info->end_of_file_info.in.size;
417 break;
419 case RAW_SFILEINFO_POSITION_INFORMATION:
420 h->position = info->position_information.in.position;
421 break;
423 case RAW_SFILEINFO_FULL_EA_INFORMATION:
424 return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd,
425 info->full_ea_information.in.eas.num_eas,
426 info->full_ea_information.in.eas.eas);
428 case RAW_SFILEINFO_MODE_INFORMATION:
429 /* this one is a puzzle */
430 if (info->mode_information.in.mode != 0 &&
431 info->mode_information.in.mode != 2 &&
432 info->mode_information.in.mode != 4 &&
433 info->mode_information.in.mode != 6) {
434 return NT_STATUS_INVALID_PARAMETER;
436 h->mode = info->mode_information.in.mode;
437 break;
439 case RAW_SFILEINFO_RENAME_INFORMATION:
440 case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
441 return pvfs_setfileinfo_rename(pvfs, req, h->name, f->handle->fd,
442 &h->odb_locking_key,
443 info);
445 case RAW_SFILEINFO_SEC_DESC:
446 notify_trigger(pvfs->notify_context,
447 NOTIFY_ACTION_MODIFIED,
448 FILE_NOTIFY_CHANGE_SECURITY,
449 h->name->full_name);
450 return pvfs_acl_set(pvfs, req, h->name, h->fd, f->access_mask, info);
452 default:
453 return NT_STATUS_INVALID_LEVEL;
456 /* possibly change the file size */
457 if (newstats.st.st_size != h->name->st.st_size) {
458 if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
459 return NT_STATUS_FILE_IS_A_DIRECTORY;
461 if (h->name->stream_name) {
462 status = pvfs_stream_truncate(pvfs, h->name, h->fd, newstats.st.st_size);
463 if (!NT_STATUS_IS_OK(status)) {
464 return status;
467 change_mask |= FILE_NOTIFY_CHANGE_STREAM_SIZE;
468 } else {
469 int ret;
470 if (f->access_mask &
471 (SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA)) {
472 ret = ftruncate(h->fd, newstats.st.st_size);
473 } else {
474 ret = truncate(h->name->full_name, newstats.st.st_size);
476 if (ret == -1) {
477 return pvfs_map_errno(pvfs, errno);
479 change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
483 /* possibly change the file timestamps */
484 if (newstats.dos.create_time != h->name->dos.create_time) {
485 change_mask |= FILE_NOTIFY_CHANGE_CREATION;
487 if (newstats.dos.access_time != h->name->dos.access_time) {
488 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
490 if (newstats.dos.write_time != h->name->dos.write_time) {
491 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
493 if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
494 (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
495 struct timeval tv[2];
497 nttime_to_timeval(&tv[0], newstats.dos.access_time);
498 nttime_to_timeval(&tv[1], newstats.dos.write_time);
500 if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
501 if (utimes(h->name->full_name, tv) == -1) {
502 DEBUG(0,("pvfs_setfileinfo: utimes() failed '%s' - %s\n",
503 h->name->full_name, strerror(errno)));
504 return pvfs_map_errno(pvfs, errno);
508 if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
509 struct odb_lock *lck;
511 lck = odb_lock(req, h->pvfs->odb_context, &h->odb_locking_key);
512 if (lck == NULL) {
513 DEBUG(0,("Unable to lock opendb for write time update\n"));
514 return NT_STATUS_INTERNAL_ERROR;
517 status = odb_set_write_time(lck, newstats.dos.write_time, true);
518 if (!NT_STATUS_IS_OK(status)) {
519 DEBUG(0,("Unable to update write time: %s\n",
520 nt_errstr(status)));
521 talloc_free(lck);
522 return status;
525 talloc_free(lck);
527 h->write_time.update_forced = true;
528 h->write_time.update_on_close = false;
529 talloc_free(h->write_time.update_event);
530 h->write_time.update_event = NULL;
533 /* possibly change the attribute */
534 if (newstats.dos.attrib != h->name->dos.attrib) {
535 mode_t mode;
536 if ((newstats.dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
537 !(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
538 return NT_STATUS_INVALID_PARAMETER;
540 mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
541 if (!(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
542 if (pvfs_sys_fchmod(pvfs, h->fd, mode, h->name->allow_override) == -1) {
543 return pvfs_map_errno(pvfs, errno);
546 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
549 *h->name = newstats;
551 notify_trigger(pvfs->notify_context,
552 NOTIFY_ACTION_MODIFIED,
553 change_mask,
554 h->name->full_name);
556 return pvfs_dosattrib_save(pvfs, h->name, h->fd);
560 retry an open after a sharing violation
562 static void pvfs_retry_setpathinfo(struct pvfs_odb_retry *r,
563 struct ntvfs_module_context *ntvfs,
564 struct ntvfs_request *req,
565 void *_info,
566 void *private_data,
567 enum pvfs_wait_notice reason)
569 union smb_setfileinfo *info = talloc_get_type(_info,
570 union smb_setfileinfo);
571 NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
573 talloc_free(r);
575 switch (reason) {
576 case PVFS_WAIT_CANCEL:
577 /*TODO*/
578 status = NT_STATUS_CANCELLED;
579 break;
580 case PVFS_WAIT_TIMEOUT:
581 /* if it timed out, then give the failure
582 immediately */
583 /*TODO*/
584 status = NT_STATUS_SHARING_VIOLATION;
585 break;
586 case PVFS_WAIT_EVENT:
588 /* try the open again, which could trigger another retry setup
589 if it wants to, so we have to unmark the async flag so we
590 will know if it does a second async reply */
591 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
593 status = pvfs_setpathinfo(ntvfs, req, info);
594 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
595 /* the 2nd try also replied async, so we don't send
596 the reply yet */
597 return;
600 /* re-mark it async, just in case someone up the chain does
601 paranoid checking */
602 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
603 break;
606 /* send the reply up the chain */
607 req->async_states->status = status;
608 req->async_states->send_fn(req);
612 setup for a unlink retry after a sharing violation
613 or a non granted oplock
615 static NTSTATUS pvfs_setpathinfo_setup_retry(struct ntvfs_module_context *ntvfs,
616 struct ntvfs_request *req,
617 union smb_setfileinfo *info,
618 struct odb_lock *lck,
619 NTSTATUS status)
621 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
622 struct pvfs_state);
623 struct timeval end_time;
625 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
626 end_time = timeval_add(&req->statistics.request_time,
627 0, pvfs->sharing_violation_delay);
628 } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
629 end_time = timeval_add(&req->statistics.request_time,
630 pvfs->oplock_break_timeout, 0);
631 } else {
632 return NT_STATUS_INTERNAL_ERROR;
635 return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, info, NULL,
636 pvfs_retry_setpathinfo);
640 set info on a pathname
642 NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
643 struct ntvfs_request *req, union smb_setfileinfo *info)
645 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
646 struct pvfs_state);
647 struct pvfs_filename *name;
648 struct pvfs_filename newstats;
649 NTSTATUS status;
650 uint32_t access_needed;
651 uint32_t change_mask = 0;
652 struct odb_lock *lck = NULL;
653 DATA_BLOB odb_locking_key;
655 /* resolve the cifs name to a posix name */
656 status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path,
657 PVFS_RESOLVE_STREAMS, &name);
658 if (!NT_STATUS_IS_OK(status)) {
659 return status;
662 if (!name->exists) {
663 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
666 access_needed = pvfs_setfileinfo_access(info);
667 status = pvfs_access_check_simple(pvfs, req, name, access_needed);
668 if (!NT_STATUS_IS_OK(status)) {
669 return status;
672 /* we take a copy of the current file stats, then update
673 newstats in each of the elements below. At the end we
674 compare, and make any changes needed */
675 newstats = *name;
677 switch (info->generic.level) {
678 case RAW_SFILEINFO_SETATTR:
679 if (!null_time(info->setattr.in.write_time)) {
680 unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
682 if (info->setattr.in.attrib == 0) {
683 newstats.dos.attrib = FILE_ATTRIBUTE_NORMAL;
684 } else if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
685 newstats.dos.attrib = info->setattr.in.attrib;
687 break;
689 case RAW_SFILEINFO_SETATTRE:
690 case RAW_SFILEINFO_STANDARD:
691 if (!null_time(info->setattre.in.create_time)) {
692 unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
694 if (!null_time(info->setattre.in.access_time)) {
695 unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
697 if (!null_time(info->setattre.in.write_time)) {
698 unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
700 break;
702 case RAW_SFILEINFO_EA_SET:
703 return pvfs_setfileinfo_ea_set(pvfs, name, -1,
704 info->ea_set.in.num_eas,
705 info->ea_set.in.eas);
707 case RAW_SFILEINFO_BASIC_INFO:
708 case RAW_SFILEINFO_BASIC_INFORMATION:
709 if (!null_nttime(info->basic_info.in.create_time)) {
710 newstats.dos.create_time = info->basic_info.in.create_time;
712 if (!null_nttime(info->basic_info.in.access_time)) {
713 newstats.dos.access_time = info->basic_info.in.access_time;
715 if (!null_nttime(info->basic_info.in.write_time)) {
716 newstats.dos.write_time = info->basic_info.in.write_time;
718 if (!null_nttime(info->basic_info.in.change_time)) {
719 newstats.dos.change_time = info->basic_info.in.change_time;
721 if (info->basic_info.in.attrib != 0) {
722 newstats.dos.attrib = info->basic_info.in.attrib;
724 break;
726 case RAW_SFILEINFO_ALLOCATION_INFO:
727 case RAW_SFILEINFO_ALLOCATION_INFORMATION:
728 status = pvfs_can_update_file_size(pvfs, req, name, &lck);
730 * on a sharing violation we need to retry when the file is closed by
731 * the other user, or after 1 second
732 * on a non granted oplock we need to retry when the file is closed by
733 * the other user, or after 30 seconds
735 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
736 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
737 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
738 return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
740 NT_STATUS_NOT_OK_RETURN(status);
742 if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
743 /* strange. Increasing the allocation size via setpathinfo
744 should be silently ignored */
745 break;
747 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
748 if (newstats.dos.alloc_size < newstats.st.st_size) {
749 newstats.st.st_size = newstats.dos.alloc_size;
751 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs,
752 newstats.dos.alloc_size);
753 break;
755 case RAW_SFILEINFO_END_OF_FILE_INFO:
756 case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
757 status = pvfs_can_update_file_size(pvfs, req, name, &lck);
759 * on a sharing violation we need to retry when the file is closed by
760 * the other user, or after 1 second
761 * on a non granted oplock we need to retry when the file is closed by
762 * the other user, or after 30 seconds
764 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
765 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
766 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
767 return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
769 NT_STATUS_NOT_OK_RETURN(status);
771 newstats.st.st_size = info->end_of_file_info.in.size;
772 break;
774 case RAW_SFILEINFO_MODE_INFORMATION:
775 if (info->mode_information.in.mode != 0 &&
776 info->mode_information.in.mode != 2 &&
777 info->mode_information.in.mode != 4 &&
778 info->mode_information.in.mode != 6) {
779 return NT_STATUS_INVALID_PARAMETER;
781 return NT_STATUS_OK;
783 case RAW_SFILEINFO_RENAME_INFORMATION:
784 case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
785 status = pvfs_locking_key(name, name, &odb_locking_key);
786 NT_STATUS_NOT_OK_RETURN(status);
787 status = pvfs_setfileinfo_rename(pvfs, req, name, -1,
788 &odb_locking_key, info);
789 NT_STATUS_NOT_OK_RETURN(status);
790 return NT_STATUS_OK;
792 case RAW_SFILEINFO_DISPOSITION_INFO:
793 case RAW_SFILEINFO_DISPOSITION_INFORMATION:
794 case RAW_SFILEINFO_POSITION_INFORMATION:
795 return NT_STATUS_OK;
797 default:
798 return NT_STATUS_INVALID_LEVEL;
801 /* possibly change the file size */
802 if (newstats.st.st_size != name->st.st_size) {
803 if (name->stream_name) {
804 status = pvfs_stream_truncate(pvfs, name, -1, newstats.st.st_size);
805 if (!NT_STATUS_IS_OK(status)) {
806 return status;
808 } else if (truncate(name->full_name, newstats.st.st_size) == -1) {
809 return pvfs_map_errno(pvfs, errno);
811 change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
814 /* possibly change the file timestamps */
815 if (newstats.dos.create_time != name->dos.create_time) {
816 change_mask |= FILE_NOTIFY_CHANGE_CREATION;
818 if (newstats.dos.access_time != name->dos.access_time) {
819 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
821 if (newstats.dos.write_time != name->dos.write_time) {
822 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
824 if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
825 (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
826 struct timeval tv[2];
828 nttime_to_timeval(&tv[0], newstats.dos.access_time);
829 nttime_to_timeval(&tv[1], newstats.dos.write_time);
831 if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
832 if (utimes(name->full_name, tv) == -1) {
833 DEBUG(0,("pvfs_setpathinfo: utimes() failed '%s' - %s\n",
834 name->full_name, strerror(errno)));
835 return pvfs_map_errno(pvfs, errno);
839 if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
840 if (lck == NULL) {
841 DATA_BLOB lkey;
842 status = pvfs_locking_key(name, name, &lkey);
843 NT_STATUS_NOT_OK_RETURN(status);
845 lck = odb_lock(req, pvfs->odb_context, &lkey);
846 data_blob_free(&lkey);
847 if (lck == NULL) {
848 DEBUG(0,("Unable to lock opendb for write time update\n"));
849 return NT_STATUS_INTERNAL_ERROR;
853 status = odb_set_write_time(lck, newstats.dos.write_time, true);
854 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
855 /* it could be that nobody has opened the file */
856 } else if (!NT_STATUS_IS_OK(status)) {
857 DEBUG(0,("Unable to update write time: %s\n",
858 nt_errstr(status)));
859 return status;
863 /* possibly change the attribute */
864 newstats.dos.attrib |= (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY);
865 if (newstats.dos.attrib != name->dos.attrib) {
866 mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
867 if (pvfs_sys_chmod(pvfs, name->full_name, mode, name->allow_override) == -1) {
868 return pvfs_map_errno(pvfs, errno);
870 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
873 *name = newstats;
875 if (change_mask != 0) {
876 notify_trigger(pvfs->notify_context,
877 NOTIFY_ACTION_MODIFIED,
878 change_mask,
879 name->full_name);
882 return pvfs_dosattrib_save(pvfs, name, -1);