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/>.
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
)
35 switch (info
->generic
.level
) {
36 case RAW_SFILEINFO_EA_SET
:
37 needed
= SEC_FILE_WRITE_EA
;
40 case RAW_SFILEINFO_DISPOSITION_INFO
:
41 case RAW_SFILEINFO_DISPOSITION_INFORMATION
:
42 needed
= SEC_STD_DELETE
;
45 case RAW_SFILEINFO_END_OF_FILE_INFO
:
46 needed
= SEC_FILE_WRITE_DATA
;
49 case RAW_SFILEINFO_POSITION_INFORMATION
:
53 case RAW_SFILEINFO_SEC_DESC
:
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
;
66 case RAW_SFILEINFO_RENAME_INFORMATION
:
67 case RAW_SFILEINFO_RENAME_INFORMATION_SMB2
:
68 needed
= SEC_STD_DELETE
;
72 needed
= SEC_FILE_WRITE_ATTRIBUTE
;
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
,
86 DATA_BLOB
*odb_locking_key
,
87 union smb_setfileinfo
*info
)
90 struct odb_lock
*lck
= NULL
;
92 /* strangely, this gives a sharing violation, not invalid
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
)) {
103 lck
= odb_lock(req
, pvfs
->odb_context
, odb_locking_key
);
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
);
117 rename_information level
119 static NTSTATUS
pvfs_setfileinfo_rename(struct pvfs_state
*pvfs
,
120 struct ntvfs_request
*req
,
121 struct pvfs_filename
*name
,
123 DATA_BLOB
*odb_locking_key
,
124 union smb_setfileinfo
*info
)
127 struct pvfs_filename
*name2
;
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
);
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
, '\\');
161 return NT_STATUS_OBJECT_NAME_INVALID
;
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
)) {
179 /* if the destination exists, then check the rename is allowed */
181 if (strcmp(name2
->full_name
, name
->full_name
) == 0) {
182 /* rename to same name is null-op */
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
)) {
202 status
= pvfs_access_check_parent(pvfs
, req
, name2
, SEC_DIR_ADD_FILE
);
203 if (!NT_STATUS_IS_OK(status
)) {
207 lck
= odb_lock(req
, pvfs
->odb_context
, odb_locking_key
);
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
);
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
);
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
;
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
)) {
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
;
262 if (i
==ealist
->num_eas
) {
264 ealist
->eas
= talloc_realloc(ealist
, ealist
->eas
,
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
;
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
],
281 (ealist
->num_eas
-(i
+1)) * sizeof(ealist
->eas
[i
]));
287 status
= pvfs_doseas_save(pvfs
, name
, fd
, ealist
);
288 if (!NT_STATUS_IS_OK(status
)) {
292 notify_trigger(pvfs
->notify_context
,
293 NOTIFY_ACTION_MODIFIED
,
294 FILE_NOTIFY_CHANGE_EA
,
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
,
317 struct pvfs_file_handle
*h
;
318 struct pvfs_filename newstats
;
320 uint32_t access_needed
;
321 uint32_t change_mask
= 0;
323 f
= pvfs_find_fd(pvfs
, req
, info
->generic
.in
.file
.ntvfs
);
325 return NT_STATUS_INVALID_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
)) {
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 */
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
;
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
);
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
;
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
);
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
;
419 case RAW_SFILEINFO_POSITION_INFORMATION
:
420 h
->position
= info
->position_information
.in
.position
;
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
;
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
,
445 case RAW_SFILEINFO_SEC_DESC
:
446 notify_trigger(pvfs
->notify_context
,
447 NOTIFY_ACTION_MODIFIED
,
448 FILE_NOTIFY_CHANGE_SECURITY
,
450 return pvfs_acl_set(pvfs
, req
, h
->name
, h
->fd
, f
->access_mask
, info
);
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
)) {
467 change_mask
|= FILE_NOTIFY_CHANGE_STREAM_SIZE
;
471 (SEC_FILE_WRITE_DATA
|SEC_FILE_APPEND_DATA
)) {
472 ret
= ftruncate(h
->fd
, newstats
.st
.st_size
);
474 ret
= truncate(h
->name
->full_name
, newstats
.st
.st_size
);
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
);
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",
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
) {
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
;
551 notify_trigger(pvfs
->notify_context
,
552 NOTIFY_ACTION_MODIFIED
,
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
,
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
;
576 case PVFS_WAIT_CANCEL
:
578 status
= NT_STATUS_CANCELLED
;
580 case PVFS_WAIT_TIMEOUT
:
581 /* if it timed out, then give the failure
584 status
= NT_STATUS_SHARING_VIOLATION
;
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
600 /* re-mark it async, just in case someone up the chain does
602 req
->async_states
->state
|= NTVFS_ASYNC_STATE_ASYNC
;
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
,
621 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
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);
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
,
647 struct pvfs_filename
*name
;
648 struct pvfs_filename newstats
;
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
)) {
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
)) {
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 */
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
;
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
);
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
;
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 */
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
);
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
;
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
;
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
);
792 case RAW_SFILEINFO_DISPOSITION_INFO
:
793 case RAW_SFILEINFO_DISPOSITION_INFORMATION
:
794 case RAW_SFILEINFO_POSITION_INFORMATION
:
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
)) {
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
) {
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
);
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",
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
;
875 if (change_mask
!= 0) {
876 notify_trigger(pvfs
->notify_context
,
877 NOTIFY_ACTION_MODIFIED
,
882 return pvfs_dosattrib_save(pvfs
, name
, -1);