s3:torture:delete: fix a comment
[Samba/gebeck_regimport.git] / source4 / ntvfs / posix / pvfs_setfileinfo.c
blob3c9b18d91ce5f7f8dc995afd9e9f2a400bd3b87f
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_MODE_INFORMATION:
424 /* this one is a puzzle */
425 if (info->mode_information.in.mode != 0 &&
426 info->mode_information.in.mode != 2 &&
427 info->mode_information.in.mode != 4 &&
428 info->mode_information.in.mode != 6) {
429 return NT_STATUS_INVALID_PARAMETER;
431 h->mode = info->mode_information.in.mode;
432 break;
434 case RAW_SFILEINFO_RENAME_INFORMATION:
435 case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
436 return pvfs_setfileinfo_rename(pvfs, req, h->name, f->handle->fd,
437 &h->odb_locking_key,
438 info);
440 case RAW_SFILEINFO_SEC_DESC:
441 notify_trigger(pvfs->notify_context,
442 NOTIFY_ACTION_MODIFIED,
443 FILE_NOTIFY_CHANGE_SECURITY,
444 h->name->full_name);
445 return pvfs_acl_set(pvfs, req, h->name, h->fd, f->access_mask, info);
447 default:
448 return NT_STATUS_INVALID_LEVEL;
451 /* possibly change the file size */
452 if (newstats.st.st_size != h->name->st.st_size) {
453 if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
454 return NT_STATUS_FILE_IS_A_DIRECTORY;
456 if (h->name->stream_name) {
457 status = pvfs_stream_truncate(pvfs, h->name, h->fd, newstats.st.st_size);
458 if (!NT_STATUS_IS_OK(status)) {
459 return status;
462 change_mask |= FILE_NOTIFY_CHANGE_STREAM_SIZE;
463 } else {
464 int ret;
465 if (f->access_mask &
466 (SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA)) {
467 ret = ftruncate(h->fd, newstats.st.st_size);
468 } else {
469 ret = truncate(h->name->full_name, newstats.st.st_size);
471 if (ret == -1) {
472 return pvfs_map_errno(pvfs, errno);
474 change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
478 /* possibly change the file timestamps */
479 if (newstats.dos.create_time != h->name->dos.create_time) {
480 change_mask |= FILE_NOTIFY_CHANGE_CREATION;
482 if (newstats.dos.access_time != h->name->dos.access_time) {
483 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
485 if (newstats.dos.write_time != h->name->dos.write_time) {
486 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
488 if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
489 (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
490 struct timeval tv[2];
492 nttime_to_timeval(&tv[0], newstats.dos.access_time);
493 nttime_to_timeval(&tv[1], newstats.dos.write_time);
495 if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
496 if (utimes(h->name->full_name, tv) == -1) {
497 DEBUG(0,("pvfs_setfileinfo: utimes() failed '%s' - %s\n",
498 h->name->full_name, strerror(errno)));
499 return pvfs_map_errno(pvfs, errno);
503 if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
504 struct odb_lock *lck;
506 lck = odb_lock(req, h->pvfs->odb_context, &h->odb_locking_key);
507 if (lck == NULL) {
508 DEBUG(0,("Unable to lock opendb for write time update\n"));
509 return NT_STATUS_INTERNAL_ERROR;
512 status = odb_set_write_time(lck, newstats.dos.write_time, true);
513 if (!NT_STATUS_IS_OK(status)) {
514 DEBUG(0,("Unable to update write time: %s\n",
515 nt_errstr(status)));
516 talloc_free(lck);
517 return status;
520 talloc_free(lck);
522 h->write_time.update_forced = true;
523 h->write_time.update_on_close = false;
524 talloc_free(h->write_time.update_event);
525 h->write_time.update_event = NULL;
528 /* possibly change the attribute */
529 if (newstats.dos.attrib != h->name->dos.attrib) {
530 mode_t mode;
531 if ((newstats.dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
532 !(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
533 return NT_STATUS_INVALID_PARAMETER;
535 mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
536 if (!(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
537 if (pvfs_sys_fchmod(pvfs, h->fd, mode, h->name->allow_override) == -1) {
538 return pvfs_map_errno(pvfs, errno);
541 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
544 *h->name = newstats;
546 notify_trigger(pvfs->notify_context,
547 NOTIFY_ACTION_MODIFIED,
548 change_mask,
549 h->name->full_name);
551 return pvfs_dosattrib_save(pvfs, h->name, h->fd);
555 retry an open after a sharing violation
557 static void pvfs_retry_setpathinfo(struct pvfs_odb_retry *r,
558 struct ntvfs_module_context *ntvfs,
559 struct ntvfs_request *req,
560 void *_info,
561 void *private_data,
562 enum pvfs_wait_notice reason)
564 union smb_setfileinfo *info = talloc_get_type(_info,
565 union smb_setfileinfo);
566 NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
568 talloc_free(r);
570 switch (reason) {
571 case PVFS_WAIT_CANCEL:
572 /*TODO*/
573 status = NT_STATUS_CANCELLED;
574 break;
575 case PVFS_WAIT_TIMEOUT:
576 /* if it timed out, then give the failure
577 immediately */
578 /*TODO*/
579 status = NT_STATUS_SHARING_VIOLATION;
580 break;
581 case PVFS_WAIT_EVENT:
583 /* try the open again, which could trigger another retry setup
584 if it wants to, so we have to unmark the async flag so we
585 will know if it does a second async reply */
586 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
588 status = pvfs_setpathinfo(ntvfs, req, info);
589 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
590 /* the 2nd try also replied async, so we don't send
591 the reply yet */
592 return;
595 /* re-mark it async, just in case someone up the chain does
596 paranoid checking */
597 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
598 break;
601 /* send the reply up the chain */
602 req->async_states->status = status;
603 req->async_states->send_fn(req);
607 setup for a unlink retry after a sharing violation
608 or a non granted oplock
610 static NTSTATUS pvfs_setpathinfo_setup_retry(struct ntvfs_module_context *ntvfs,
611 struct ntvfs_request *req,
612 union smb_setfileinfo *info,
613 struct odb_lock *lck,
614 NTSTATUS status)
616 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
617 struct pvfs_state);
618 struct timeval end_time;
620 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
621 end_time = timeval_add(&req->statistics.request_time,
622 0, pvfs->sharing_violation_delay);
623 } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
624 end_time = timeval_add(&req->statistics.request_time,
625 pvfs->oplock_break_timeout, 0);
626 } else {
627 return NT_STATUS_INTERNAL_ERROR;
630 return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, info, NULL,
631 pvfs_retry_setpathinfo);
635 set info on a pathname
637 NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
638 struct ntvfs_request *req, union smb_setfileinfo *info)
640 struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
641 struct pvfs_state);
642 struct pvfs_filename *name;
643 struct pvfs_filename newstats;
644 NTSTATUS status;
645 uint32_t access_needed;
646 uint32_t change_mask = 0;
647 struct odb_lock *lck = NULL;
648 DATA_BLOB odb_locking_key;
650 /* resolve the cifs name to a posix name */
651 status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path,
652 PVFS_RESOLVE_STREAMS, &name);
653 if (!NT_STATUS_IS_OK(status)) {
654 return status;
657 if (!name->exists) {
658 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
661 access_needed = pvfs_setfileinfo_access(info);
662 status = pvfs_access_check_simple(pvfs, req, name, access_needed);
663 if (!NT_STATUS_IS_OK(status)) {
664 return status;
667 /* we take a copy of the current file stats, then update
668 newstats in each of the elements below. At the end we
669 compare, and make any changes needed */
670 newstats = *name;
672 switch (info->generic.level) {
673 case RAW_SFILEINFO_SETATTR:
674 if (!null_time(info->setattr.in.write_time)) {
675 unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
677 if (info->setattr.in.attrib == 0) {
678 newstats.dos.attrib = FILE_ATTRIBUTE_NORMAL;
679 } else if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
680 newstats.dos.attrib = info->setattr.in.attrib;
682 break;
684 case RAW_SFILEINFO_SETATTRE:
685 case RAW_SFILEINFO_STANDARD:
686 if (!null_time(info->setattre.in.create_time)) {
687 unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
689 if (!null_time(info->setattre.in.access_time)) {
690 unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
692 if (!null_time(info->setattre.in.write_time)) {
693 unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
695 break;
697 case RAW_SFILEINFO_EA_SET:
698 return pvfs_setfileinfo_ea_set(pvfs, name, -1,
699 info->ea_set.in.num_eas,
700 info->ea_set.in.eas);
702 case RAW_SFILEINFO_BASIC_INFO:
703 case RAW_SFILEINFO_BASIC_INFORMATION:
704 if (!null_nttime(info->basic_info.in.create_time)) {
705 newstats.dos.create_time = info->basic_info.in.create_time;
707 if (!null_nttime(info->basic_info.in.access_time)) {
708 newstats.dos.access_time = info->basic_info.in.access_time;
710 if (!null_nttime(info->basic_info.in.write_time)) {
711 newstats.dos.write_time = info->basic_info.in.write_time;
713 if (!null_nttime(info->basic_info.in.change_time)) {
714 newstats.dos.change_time = info->basic_info.in.change_time;
716 if (info->basic_info.in.attrib != 0) {
717 newstats.dos.attrib = info->basic_info.in.attrib;
719 break;
721 case RAW_SFILEINFO_ALLOCATION_INFO:
722 case RAW_SFILEINFO_ALLOCATION_INFORMATION:
723 status = pvfs_can_update_file_size(pvfs, req, name, &lck);
725 * on a sharing violation we need to retry when the file is closed by
726 * the other user, or after 1 second
727 * on a non granted oplock we need to retry when the file is closed by
728 * the other user, or after 30 seconds
730 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
731 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
732 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
733 return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
735 NT_STATUS_NOT_OK_RETURN(status);
737 if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
738 /* strange. Increasing the allocation size via setpathinfo
739 should be silently ignored */
740 break;
742 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
743 if (newstats.dos.alloc_size < newstats.st.st_size) {
744 newstats.st.st_size = newstats.dos.alloc_size;
746 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs,
747 newstats.dos.alloc_size);
748 break;
750 case RAW_SFILEINFO_END_OF_FILE_INFO:
751 case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
752 status = pvfs_can_update_file_size(pvfs, req, name, &lck);
754 * on a sharing violation we need to retry when the file is closed by
755 * the other user, or after 1 second
756 * on a non granted oplock we need to retry when the file is closed by
757 * the other user, or after 30 seconds
759 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
760 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
761 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
762 return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
764 NT_STATUS_NOT_OK_RETURN(status);
766 newstats.st.st_size = info->end_of_file_info.in.size;
767 break;
769 case RAW_SFILEINFO_MODE_INFORMATION:
770 if (info->mode_information.in.mode != 0 &&
771 info->mode_information.in.mode != 2 &&
772 info->mode_information.in.mode != 4 &&
773 info->mode_information.in.mode != 6) {
774 return NT_STATUS_INVALID_PARAMETER;
776 return NT_STATUS_OK;
778 case RAW_SFILEINFO_RENAME_INFORMATION:
779 case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
780 status = pvfs_locking_key(name, name, &odb_locking_key);
781 NT_STATUS_NOT_OK_RETURN(status);
782 status = pvfs_setfileinfo_rename(pvfs, req, name, -1,
783 &odb_locking_key, info);
784 NT_STATUS_NOT_OK_RETURN(status);
785 return NT_STATUS_OK;
787 case RAW_SFILEINFO_DISPOSITION_INFO:
788 case RAW_SFILEINFO_DISPOSITION_INFORMATION:
789 case RAW_SFILEINFO_POSITION_INFORMATION:
790 return NT_STATUS_OK;
792 default:
793 return NT_STATUS_INVALID_LEVEL;
796 /* possibly change the file size */
797 if (newstats.st.st_size != name->st.st_size) {
798 if (name->stream_name) {
799 status = pvfs_stream_truncate(pvfs, name, -1, newstats.st.st_size);
800 if (!NT_STATUS_IS_OK(status)) {
801 return status;
803 } else if (truncate(name->full_name, newstats.st.st_size) == -1) {
804 return pvfs_map_errno(pvfs, errno);
806 change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
809 /* possibly change the file timestamps */
810 if (newstats.dos.create_time != name->dos.create_time) {
811 change_mask |= FILE_NOTIFY_CHANGE_CREATION;
813 if (newstats.dos.access_time != name->dos.access_time) {
814 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
816 if (newstats.dos.write_time != name->dos.write_time) {
817 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
819 if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
820 (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
821 struct timeval tv[2];
823 nttime_to_timeval(&tv[0], newstats.dos.access_time);
824 nttime_to_timeval(&tv[1], newstats.dos.write_time);
826 if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
827 if (utimes(name->full_name, tv) == -1) {
828 DEBUG(0,("pvfs_setpathinfo: utimes() failed '%s' - %s\n",
829 name->full_name, strerror(errno)));
830 return pvfs_map_errno(pvfs, errno);
834 if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
835 if (lck == NULL) {
836 DATA_BLOB lkey;
837 status = pvfs_locking_key(name, name, &lkey);
838 NT_STATUS_NOT_OK_RETURN(status);
840 lck = odb_lock(req, pvfs->odb_context, &lkey);
841 data_blob_free(&lkey);
842 if (lck == NULL) {
843 DEBUG(0,("Unable to lock opendb for write time update\n"));
844 return NT_STATUS_INTERNAL_ERROR;
848 status = odb_set_write_time(lck, newstats.dos.write_time, true);
849 if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
850 /* it could be that nobody has opened the file */
851 } else if (!NT_STATUS_IS_OK(status)) {
852 DEBUG(0,("Unable to update write time: %s\n",
853 nt_errstr(status)));
854 return status;
858 /* possibly change the attribute */
859 newstats.dos.attrib |= (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY);
860 if (newstats.dos.attrib != name->dos.attrib) {
861 mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
862 if (pvfs_sys_chmod(pvfs, name->full_name, mode, name->allow_override) == -1) {
863 return pvfs_map_errno(pvfs, errno);
865 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
868 *name = newstats;
870 if (change_mask != 0) {
871 notify_trigger(pvfs->notify_context,
872 NOTIFY_ACTION_MODIFIED,
873 change_mask,
874 name->full_name);
877 return pvfs_dosattrib_save(pvfs, name, -1);