don't allow a file to be changed to a directory with setfileinfo
[Samba/bb.git] / source / ntvfs / posix / pvfs_setfileinfo.c
blob1dd2c075d9abf57254552f399ecfdee4c9763e12
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
82 static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs,
83 struct ntvfs_request *req,
84 struct pvfs_filename *name,
85 DATA_BLOB *odb_locking_key,
86 union smb_setfileinfo *info)
88 NTSTATUS status;
89 struct pvfs_filename *name2;
90 char *new_name, *p;
91 struct odb_lock *lck = NULL;
93 /* renames are only allowed within a directory */
94 if (strchr_m(info->rename_information.in.new_name, '\\') &&
95 (req->ctx->protocol != PROTOCOL_SMB2)) {
96 return NT_STATUS_NOT_SUPPORTED;
99 /* don't allow stream renames for now */
100 if (name->stream_name) {
101 return NT_STATUS_INVALID_PARAMETER;
104 /* w2k3 does not appear to allow relative rename. On SMB2, vista sends it sometimes,
105 but I suspect it is just uninitialised memory */
106 if (info->rename_information.in.root_fid != 0 &&
107 (req->ctx->protocol != PROTOCOL_SMB2)) {
108 return NT_STATUS_INVALID_PARAMETER;
111 /* construct the fully qualified windows name for the new file name */
112 if (req->ctx->protocol == PROTOCOL_SMB2) {
113 /* SMB2 sends the full path of the new name */
114 new_name = talloc_asprintf(req, "\\%s", info->rename_information.in.new_name);
115 } else {
116 new_name = talloc_strdup(req, name->original_name);
117 if (new_name == NULL) {
118 return NT_STATUS_NO_MEMORY;
120 p = strrchr_m(new_name, '\\');
121 if (p == NULL) {
122 return NT_STATUS_OBJECT_NAME_INVALID;
123 } else {
124 *p = 0;
127 new_name = talloc_asprintf(req, "%s\\%s", new_name,
128 info->rename_information.in.new_name);
130 if (new_name == NULL) {
131 return NT_STATUS_NO_MEMORY;
134 /* resolve the new name */
135 status = pvfs_resolve_name(pvfs, name, new_name, 0, &name2);
136 if (!NT_STATUS_IS_OK(status)) {
137 return status;
140 /* if the destination exists, then check the rename is allowed */
141 if (name2->exists) {
142 if (strcmp(name2->full_name, name->full_name) == 0) {
143 /* rename to same name is null-op */
144 return NT_STATUS_OK;
147 if (!info->rename_information.in.overwrite) {
148 return NT_STATUS_OBJECT_NAME_COLLISION;
151 status = pvfs_can_delete(pvfs, req, name2, NULL);
152 if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) {
153 return NT_STATUS_ACCESS_DENIED;
155 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
156 return NT_STATUS_ACCESS_DENIED;
158 if (!NT_STATUS_IS_OK(status)) {
159 return status;
163 status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
164 if (!NT_STATUS_IS_OK(status)) {
165 return status;
168 lck = odb_lock(req, pvfs->odb_context, odb_locking_key);
169 if (lck == NULL) {
170 DEBUG(0,("Unable to lock opendb for can_stat\n"));
171 return NT_STATUS_INTERNAL_DB_CORRUPTION;
174 status = pvfs_do_rename(pvfs, lck, name, name2->full_name);
175 talloc_free(lck);
176 NT_STATUS_NOT_OK_RETURN(status);
177 if (NT_STATUS_IS_OK(status)) {
178 name->full_name = talloc_steal(name, name2->full_name);
179 name->original_name = talloc_steal(name, name2->original_name);
182 return NT_STATUS_OK;
186 add a single DOS EA
188 NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs,
189 struct pvfs_filename *name,
190 int fd, uint16_t num_eas,
191 struct ea_struct *eas)
193 struct xattr_DosEAs *ealist;
194 int i, j;
195 NTSTATUS status;
197 if (num_eas == 0) {
198 return NT_STATUS_OK;
201 if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
202 return NT_STATUS_NOT_SUPPORTED;
205 ealist = talloc(name, struct xattr_DosEAs);
207 /* load the current list */
208 status = pvfs_doseas_load(pvfs, name, fd, ealist);
209 if (!NT_STATUS_IS_OK(status)) {
210 return status;
213 for (j=0;j<num_eas;j++) {
214 struct ea_struct *ea = &eas[j];
215 /* see if its already there */
216 for (i=0;i<ealist->num_eas;i++) {
217 if (strcasecmp_m(ealist->eas[i].name, ea->name.s) == 0) {
218 ealist->eas[i].value = ea->value;
219 break;
223 if (i==ealist->num_eas) {
224 /* add it */
225 ealist->eas = talloc_realloc(ealist, ealist->eas,
226 struct xattr_EA,
227 ealist->num_eas+1);
228 if (ealist->eas == NULL) {
229 return NT_STATUS_NO_MEMORY;
231 ealist->eas[i].name = ea->name.s;
232 ealist->eas[i].value = ea->value;
233 ealist->num_eas++;
237 /* pull out any null EAs */
238 for (i=0;i<ealist->num_eas;i++) {
239 if (ealist->eas[i].value.length == 0) {
240 memmove(&ealist->eas[i],
241 &ealist->eas[i+1],
242 (ealist->num_eas-(i+1)) * sizeof(ealist->eas[i]));
243 ealist->num_eas--;
244 i--;
248 status = pvfs_doseas_save(pvfs, name, fd, ealist);
249 if (!NT_STATUS_IS_OK(status)) {
250 return status;
253 notify_trigger(pvfs->notify_context,
254 NOTIFY_ACTION_MODIFIED,
255 FILE_NOTIFY_CHANGE_EA,
256 name->full_name);
258 name->dos.ea_size = 4;
259 for (i=0;i<ealist->num_eas;i++) {
260 name->dos.ea_size += 4 + strlen(ealist->eas[i].name)+1 +
261 ealist->eas[i].value.length;
264 /* update the ea_size attrib */
265 return pvfs_dosattrib_save(pvfs, name, fd);
269 set info on a open file
271 NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
272 struct ntvfs_request *req,
273 union smb_setfileinfo *info)
275 struct pvfs_state *pvfs = ntvfs->private_data;
276 struct utimbuf unix_times;
277 struct pvfs_file *f;
278 struct pvfs_file_handle *h;
279 struct pvfs_filename newstats;
280 NTSTATUS status;
281 uint32_t access_needed;
282 uint32_t change_mask = 0;
284 f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
285 if (!f) {
286 return NT_STATUS_INVALID_HANDLE;
289 h = f->handle;
291 access_needed = pvfs_setfileinfo_access(info);
292 if ((f->access_mask & access_needed) != access_needed) {
293 return NT_STATUS_ACCESS_DENIED;
296 /* update the file information */
297 status = pvfs_resolve_name_handle(pvfs, h);
298 if (!NT_STATUS_IS_OK(status)) {
299 return status;
302 /* we take a copy of the current file stats, then update
303 newstats in each of the elements below. At the end we
304 compare, and make any changes needed */
305 newstats = *h->name;
307 switch (info->generic.level) {
308 case RAW_SFILEINFO_SETATTR:
309 if (!null_time(info->setattr.in.write_time)) {
310 unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
312 if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
313 newstats.dos.attrib = info->setattr.in.attrib;
315 break;
317 case RAW_SFILEINFO_SETATTRE:
318 case RAW_SFILEINFO_STANDARD:
319 if (!null_time(info->setattre.in.create_time)) {
320 unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
322 if (!null_time(info->setattre.in.access_time)) {
323 unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
325 if (!null_time(info->setattre.in.write_time)) {
326 unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
328 break;
330 case RAW_SFILEINFO_EA_SET:
331 return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd,
332 info->ea_set.in.num_eas,
333 info->ea_set.in.eas);
335 case RAW_SFILEINFO_BASIC_INFO:
336 case RAW_SFILEINFO_BASIC_INFORMATION:
337 if (!null_nttime(info->basic_info.in.create_time)) {
338 newstats.dos.create_time = info->basic_info.in.create_time;
340 if (!null_nttime(info->basic_info.in.access_time)) {
341 newstats.dos.access_time = info->basic_info.in.access_time;
343 if (!null_nttime(info->basic_info.in.write_time)) {
344 newstats.dos.write_time = info->basic_info.in.write_time;
346 if (!null_nttime(info->basic_info.in.change_time)) {
347 newstats.dos.change_time = info->basic_info.in.change_time;
349 if (info->basic_info.in.attrib != 0) {
350 newstats.dos.attrib = info->basic_info.in.attrib;
352 break;
354 case RAW_SFILEINFO_DISPOSITION_INFO:
355 case RAW_SFILEINFO_DISPOSITION_INFORMATION:
356 return pvfs_set_delete_on_close(pvfs, req, f,
357 info->disposition_info.in.delete_on_close);
359 case RAW_SFILEINFO_ALLOCATION_INFO:
360 case RAW_SFILEINFO_ALLOCATION_INFORMATION:
361 status = pvfs_break_level2_oplocks(f);
362 NT_STATUS_NOT_OK_RETURN(status);
364 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
365 if (newstats.dos.alloc_size < newstats.st.st_size) {
366 newstats.st.st_size = newstats.dos.alloc_size;
368 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs,
369 newstats.dos.alloc_size);
370 break;
372 case RAW_SFILEINFO_END_OF_FILE_INFO:
373 case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
374 status = pvfs_break_level2_oplocks(f);
375 NT_STATUS_NOT_OK_RETURN(status);
377 newstats.st.st_size = info->end_of_file_info.in.size;
378 break;
380 case RAW_SFILEINFO_POSITION_INFORMATION:
381 h->position = info->position_information.in.position;
382 break;
384 case RAW_SFILEINFO_MODE_INFORMATION:
385 /* this one is a puzzle */
386 if (info->mode_information.in.mode != 0 &&
387 info->mode_information.in.mode != 2 &&
388 info->mode_information.in.mode != 4 &&
389 info->mode_information.in.mode != 6) {
390 return NT_STATUS_INVALID_PARAMETER;
392 h->mode = info->mode_information.in.mode;
393 break;
395 case RAW_SFILEINFO_RENAME_INFORMATION:
396 case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
397 return pvfs_setfileinfo_rename(pvfs, req, h->name,
398 &h->odb_locking_key,
399 info);
401 case RAW_SFILEINFO_SEC_DESC:
402 notify_trigger(pvfs->notify_context,
403 NOTIFY_ACTION_MODIFIED,
404 FILE_NOTIFY_CHANGE_SECURITY,
405 h->name->full_name);
406 return pvfs_acl_set(pvfs, req, h->name, h->fd, f->access_mask, info);
408 default:
409 return NT_STATUS_INVALID_LEVEL;
412 /* possibly change the file size */
413 if (newstats.st.st_size != h->name->st.st_size) {
414 if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
415 return NT_STATUS_FILE_IS_A_DIRECTORY;
417 if (h->name->stream_name) {
418 status = pvfs_stream_truncate(pvfs, h->name, h->fd, newstats.st.st_size);
419 if (!NT_STATUS_IS_OK(status)) {
420 return status;
423 change_mask |= FILE_NOTIFY_CHANGE_STREAM_SIZE;
424 } else {
425 int ret;
426 if (f->access_mask &
427 (SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA)) {
428 ret = ftruncate(h->fd, newstats.st.st_size);
429 } else {
430 ret = truncate(h->name->full_name, newstats.st.st_size);
432 if (ret == -1) {
433 return pvfs_map_errno(pvfs, errno);
435 change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
439 /* possibly change the file timestamps */
440 ZERO_STRUCT(unix_times);
441 if (newstats.dos.create_time != h->name->dos.create_time) {
442 change_mask |= FILE_NOTIFY_CHANGE_CREATION;
444 if (newstats.dos.access_time != h->name->dos.access_time) {
445 unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
446 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
448 if (newstats.dos.write_time != h->name->dos.write_time) {
449 unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
450 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
452 if (unix_times.actime != 0 || unix_times.modtime != 0) {
453 if (utime(h->name->full_name, &unix_times) == -1) {
454 return pvfs_map_errno(pvfs, errno);
458 /* possibly change the attribute */
459 if (newstats.dos.attrib != h->name->dos.attrib) {
460 mode_t mode;
461 if ((newstats.dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
462 !(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
463 return NT_STATUS_INVALID_PARAMETER;
465 mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
466 if (!(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
467 if (fchmod(h->fd, mode) == -1) {
468 return pvfs_map_errno(pvfs, errno);
471 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
474 *h->name = newstats;
476 notify_trigger(pvfs->notify_context,
477 NOTIFY_ACTION_MODIFIED,
478 change_mask,
479 h->name->full_name);
481 return pvfs_dosattrib_save(pvfs, h->name, h->fd);
485 retry an open after a sharing violation
487 static void pvfs_retry_setpathinfo(struct pvfs_odb_retry *r,
488 struct ntvfs_module_context *ntvfs,
489 struct ntvfs_request *req,
490 void *_info,
491 void *private_data,
492 enum pvfs_wait_notice reason)
494 union smb_setfileinfo *info = talloc_get_type(_info,
495 union smb_setfileinfo);
496 NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
498 talloc_free(r);
500 switch (reason) {
501 case PVFS_WAIT_CANCEL:
502 /*TODO*/
503 status = NT_STATUS_CANCELLED;
504 break;
505 case PVFS_WAIT_TIMEOUT:
506 /* if it timed out, then give the failure
507 immediately */
508 /*TODO*/
509 status = NT_STATUS_SHARING_VIOLATION;
510 break;
511 case PVFS_WAIT_EVENT:
513 /* try the open again, which could trigger another retry setup
514 if it wants to, so we have to unmark the async flag so we
515 will know if it does a second async reply */
516 req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
518 status = pvfs_setpathinfo(ntvfs, req, info);
519 if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
520 /* the 2nd try also replied async, so we don't send
521 the reply yet */
522 return;
525 /* re-mark it async, just in case someone up the chain does
526 paranoid checking */
527 req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
528 break;
531 /* send the reply up the chain */
532 req->async_states->status = status;
533 req->async_states->send_fn(req);
537 setup for a unlink retry after a sharing violation
538 or a non granted oplock
540 static NTSTATUS pvfs_setpathinfo_setup_retry(struct ntvfs_module_context *ntvfs,
541 struct ntvfs_request *req,
542 union smb_setfileinfo *info,
543 struct odb_lock *lck,
544 NTSTATUS status)
546 struct pvfs_state *pvfs = ntvfs->private_data;
547 struct timeval end_time;
549 if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
550 end_time = timeval_add(&req->statistics.request_time,
551 0, pvfs->sharing_violation_delay);
552 } else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
553 end_time = timeval_add(&req->statistics.request_time,
554 pvfs->oplock_break_timeout, 0);
555 } else {
556 return NT_STATUS_INTERNAL_ERROR;
559 return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, info, NULL,
560 pvfs_retry_setpathinfo);
564 set info on a pathname
566 NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
567 struct ntvfs_request *req, union smb_setfileinfo *info)
569 struct pvfs_state *pvfs = ntvfs->private_data;
570 struct pvfs_filename *name;
571 struct pvfs_filename newstats;
572 NTSTATUS status;
573 struct utimbuf unix_times;
574 uint32_t access_needed;
575 uint32_t change_mask = 0;
576 struct odb_lock *lck = NULL;
577 DATA_BLOB odb_locking_key;
579 /* resolve the cifs name to a posix name */
580 status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path,
581 PVFS_RESOLVE_STREAMS, &name);
582 if (!NT_STATUS_IS_OK(status)) {
583 return status;
586 if (!name->exists) {
587 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
590 access_needed = pvfs_setfileinfo_access(info);
591 status = pvfs_access_check_simple(pvfs, req, name, access_needed);
592 if (!NT_STATUS_IS_OK(status)) {
593 return status;
596 /* we take a copy of the current file stats, then update
597 newstats in each of the elements below. At the end we
598 compare, and make any changes needed */
599 newstats = *name;
601 switch (info->generic.level) {
602 case RAW_SFILEINFO_SETATTR:
603 if (!null_time(info->setattr.in.write_time)) {
604 unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
606 if (info->setattr.in.attrib == 0) {
607 newstats.dos.attrib = FILE_ATTRIBUTE_NORMAL;
608 } else if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
609 newstats.dos.attrib = info->setattr.in.attrib;
611 break;
613 case RAW_SFILEINFO_SETATTRE:
614 case RAW_SFILEINFO_STANDARD:
615 if (!null_time(info->setattre.in.create_time)) {
616 unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
618 if (!null_time(info->setattre.in.access_time)) {
619 unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
621 if (!null_time(info->setattre.in.write_time)) {
622 unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
624 break;
626 case RAW_SFILEINFO_EA_SET:
627 return pvfs_setfileinfo_ea_set(pvfs, name, -1,
628 info->ea_set.in.num_eas,
629 info->ea_set.in.eas);
631 case RAW_SFILEINFO_BASIC_INFO:
632 case RAW_SFILEINFO_BASIC_INFORMATION:
633 if (!null_nttime(info->basic_info.in.create_time)) {
634 newstats.dos.create_time = info->basic_info.in.create_time;
636 if (!null_nttime(info->basic_info.in.access_time)) {
637 newstats.dos.access_time = info->basic_info.in.access_time;
639 if (!null_nttime(info->basic_info.in.write_time)) {
640 newstats.dos.write_time = info->basic_info.in.write_time;
642 if (!null_nttime(info->basic_info.in.change_time)) {
643 newstats.dos.change_time = info->basic_info.in.change_time;
645 if (info->basic_info.in.attrib != 0) {
646 newstats.dos.attrib = info->basic_info.in.attrib;
648 break;
650 case RAW_SFILEINFO_ALLOCATION_INFO:
651 case RAW_SFILEINFO_ALLOCATION_INFORMATION:
652 status = pvfs_can_update_file_size(pvfs, req, name, &lck);
654 * on a sharing violation we need to retry when the file is closed by
655 * the other user, or after 1 second
656 * on a non granted oplock we need to retry when the file is closed by
657 * the other user, or after 30 seconds
659 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
660 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
661 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
662 return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
664 NT_STATUS_NOT_OK_RETURN(status);
666 if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
667 /* strange. Increasing the allocation size via setpathinfo
668 should be silently ignored */
669 break;
671 newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
672 if (newstats.dos.alloc_size < newstats.st.st_size) {
673 newstats.st.st_size = newstats.dos.alloc_size;
675 newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs,
676 newstats.dos.alloc_size);
677 break;
679 case RAW_SFILEINFO_END_OF_FILE_INFO:
680 case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
681 status = pvfs_can_update_file_size(pvfs, req, name, &lck);
683 * on a sharing violation we need to retry when the file is closed by
684 * the other user, or after 1 second
685 * on a non granted oplock we need to retry when the file is closed by
686 * the other user, or after 30 seconds
688 if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
689 NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
690 (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
691 return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
693 NT_STATUS_NOT_OK_RETURN(status);
695 newstats.st.st_size = info->end_of_file_info.in.size;
696 break;
698 case RAW_SFILEINFO_MODE_INFORMATION:
699 if (info->mode_information.in.mode != 0 &&
700 info->mode_information.in.mode != 2 &&
701 info->mode_information.in.mode != 4 &&
702 info->mode_information.in.mode != 6) {
703 return NT_STATUS_INVALID_PARAMETER;
705 return NT_STATUS_OK;
707 case RAW_SFILEINFO_RENAME_INFORMATION:
708 case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
709 status = pvfs_locking_key(name, name, &odb_locking_key);
710 NT_STATUS_NOT_OK_RETURN(status);
711 status = pvfs_setfileinfo_rename(pvfs, req, name,
712 &odb_locking_key, info);
713 NT_STATUS_NOT_OK_RETURN(status);
714 return NT_STATUS_OK;
716 case RAW_SFILEINFO_DISPOSITION_INFO:
717 case RAW_SFILEINFO_DISPOSITION_INFORMATION:
718 case RAW_SFILEINFO_POSITION_INFORMATION:
719 return NT_STATUS_OK;
721 default:
722 return NT_STATUS_INVALID_LEVEL;
725 /* possibly change the file size */
726 if (newstats.st.st_size != name->st.st_size) {
727 if (name->stream_name) {
728 status = pvfs_stream_truncate(pvfs, name, -1, newstats.st.st_size);
729 if (!NT_STATUS_IS_OK(status)) {
730 return status;
732 } else if (truncate(name->full_name, newstats.st.st_size) == -1) {
733 return pvfs_map_errno(pvfs, errno);
735 change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
738 /* possibly change the file timestamps */
739 ZERO_STRUCT(unix_times);
740 if (newstats.dos.create_time != name->dos.create_time) {
741 change_mask |= FILE_NOTIFY_CHANGE_CREATION;
743 if (newstats.dos.access_time != name->dos.access_time) {
744 unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
745 change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
747 if (newstats.dos.write_time != name->dos.write_time) {
748 unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
749 change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
751 if (unix_times.actime != 0 || unix_times.modtime != 0) {
752 if (utime(name->full_name, &unix_times) == -1) {
753 return pvfs_map_errno(pvfs, errno);
757 /* possibly change the attribute */
758 newstats.dos.attrib |= (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY);
759 if (newstats.dos.attrib != name->dos.attrib) {
760 mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
761 if (chmod(name->full_name, mode) == -1) {
762 return pvfs_map_errno(pvfs, errno);
764 change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
767 *name = newstats;
769 if (change_mask != 0) {
770 notify_trigger(pvfs->notify_context,
771 NOTIFY_ACTION_MODIFIED,
772 change_mask,
773 name->full_name);
776 return pvfs_dosattrib_save(pvfs, name, -1);