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_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
;
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
,
440 case RAW_SFILEINFO_SEC_DESC
:
441 notify_trigger(pvfs
->notify_context
,
442 NOTIFY_ACTION_MODIFIED
,
443 FILE_NOTIFY_CHANGE_SECURITY
,
445 return pvfs_acl_set(pvfs
, req
, h
->name
, h
->fd
, f
->access_mask
, info
);
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
)) {
462 change_mask
|= FILE_NOTIFY_CHANGE_STREAM_SIZE
;
466 (SEC_FILE_WRITE_DATA
|SEC_FILE_APPEND_DATA
)) {
467 ret
= ftruncate(h
->fd
, newstats
.st
.st_size
);
469 ret
= truncate(h
->name
->full_name
, newstats
.st
.st_size
);
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
);
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",
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
) {
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
;
546 notify_trigger(pvfs
->notify_context
,
547 NOTIFY_ACTION_MODIFIED
,
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
,
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
;
571 case PVFS_WAIT_CANCEL
:
573 status
= NT_STATUS_CANCELLED
;
575 case PVFS_WAIT_TIMEOUT
:
576 /* if it timed out, then give the failure
579 status
= NT_STATUS_SHARING_VIOLATION
;
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
595 /* re-mark it async, just in case someone up the chain does
597 req
->async_states
->state
|= NTVFS_ASYNC_STATE_ASYNC
;
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
,
616 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
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);
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
,
642 struct pvfs_filename
*name
;
643 struct pvfs_filename newstats
;
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
)) {
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
)) {
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 */
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
;
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
);
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
;
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 */
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
);
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
;
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
;
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
);
787 case RAW_SFILEINFO_DISPOSITION_INFO
:
788 case RAW_SFILEINFO_DISPOSITION_INFORMATION
:
789 case RAW_SFILEINFO_POSITION_INFORMATION
:
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
)) {
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
) {
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
);
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",
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
;
870 if (change_mask
!= 0) {
871 notify_trigger(pvfs
->notify_context
,
872 NOTIFY_ACTION_MODIFIED
,
877 return pvfs_dosattrib_save(pvfs
, name
, -1);