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 if (info
->rename_information
.in
.new_name
[0] != ':') {
93 return NT_STATUS_INVALID_PARAMETER
;
96 status
= pvfs_access_check_simple(pvfs
, req
, name
, SEC_FILE_WRITE_ATTRIBUTE
);
97 if (!NT_STATUS_IS_OK(status
)) {
101 lck
= odb_lock(req
, pvfs
->odb_context
, odb_locking_key
);
103 DEBUG(0,("Unable to lock opendb for can_stat\n"));
104 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
108 status
= pvfs_stream_rename(pvfs
, name
, fd
,
109 info
->rename_information
.in
.new_name
+1);
114 rename_information level
116 static NTSTATUS
pvfs_setfileinfo_rename(struct pvfs_state
*pvfs
,
117 struct ntvfs_request
*req
,
118 struct pvfs_filename
*name
,
120 DATA_BLOB
*odb_locking_key
,
121 union smb_setfileinfo
*info
)
124 struct pvfs_filename
*name2
;
126 struct odb_lock
*lck
= NULL
;
128 /* renames are only allowed within a directory */
129 if (strchr_m(info
->rename_information
.in
.new_name
, '\\') &&
130 (req
->ctx
->protocol
!= PROTOCOL_SMB2
)) {
131 return NT_STATUS_NOT_SUPPORTED
;
134 /* handle stream renames specially */
135 if (name
->stream_name
) {
136 return pvfs_setfileinfo_rename_stream(pvfs
, req
, name
, fd
,
137 odb_locking_key
, info
);
140 /* w2k3 does not appear to allow relative rename. On SMB2, vista sends it sometimes,
141 but I suspect it is just uninitialised memory */
142 if (info
->rename_information
.in
.root_fid
!= 0 &&
143 (req
->ctx
->protocol
!= PROTOCOL_SMB2
)) {
144 return NT_STATUS_INVALID_PARAMETER
;
147 /* construct the fully qualified windows name for the new file name */
148 if (req
->ctx
->protocol
== PROTOCOL_SMB2
) {
149 /* SMB2 sends the full path of the new name */
150 new_name
= talloc_asprintf(req
, "\\%s", info
->rename_information
.in
.new_name
);
152 new_name
= talloc_strdup(req
, name
->original_name
);
153 if (new_name
== NULL
) {
154 return NT_STATUS_NO_MEMORY
;
156 p
= strrchr_m(new_name
, '\\');
158 return NT_STATUS_OBJECT_NAME_INVALID
;
163 new_name
= talloc_asprintf(req
, "%s\\%s", new_name
,
164 info
->rename_information
.in
.new_name
);
166 if (new_name
== NULL
) {
167 return NT_STATUS_NO_MEMORY
;
170 /* resolve the new name */
171 status
= pvfs_resolve_name(pvfs
, name
, new_name
, 0, &name2
);
172 if (!NT_STATUS_IS_OK(status
)) {
176 /* if the destination exists, then check the rename is allowed */
178 if (strcmp(name2
->full_name
, name
->full_name
) == 0) {
179 /* rename to same name is null-op */
183 if (!info
->rename_information
.in
.overwrite
) {
184 return NT_STATUS_OBJECT_NAME_COLLISION
;
187 status
= pvfs_can_delete(pvfs
, req
, name2
, NULL
);
188 if (NT_STATUS_EQUAL(status
, NT_STATUS_DELETE_PENDING
)) {
189 return NT_STATUS_ACCESS_DENIED
;
191 if (NT_STATUS_EQUAL(status
, NT_STATUS_SHARING_VIOLATION
)) {
192 return NT_STATUS_ACCESS_DENIED
;
194 if (!NT_STATUS_IS_OK(status
)) {
199 status
= pvfs_access_check_parent(pvfs
, req
, name2
, SEC_DIR_ADD_FILE
);
200 if (!NT_STATUS_IS_OK(status
)) {
204 lck
= odb_lock(req
, pvfs
->odb_context
, odb_locking_key
);
206 DEBUG(0,("Unable to lock opendb for can_stat\n"));
207 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
210 status
= pvfs_do_rename(pvfs
, lck
, name
, name2
->full_name
);
212 NT_STATUS_NOT_OK_RETURN(status
);
213 if (NT_STATUS_IS_OK(status
)) {
214 name
->full_name
= talloc_steal(name
, name2
->full_name
);
215 name
->original_name
= talloc_steal(name
, name2
->original_name
);
224 NTSTATUS
pvfs_setfileinfo_ea_set(struct pvfs_state
*pvfs
,
225 struct pvfs_filename
*name
,
226 int fd
, uint16_t num_eas
,
227 struct ea_struct
*eas
)
229 struct xattr_DosEAs
*ealist
;
237 if (!(pvfs
->flags
& PVFS_FLAG_XATTR_ENABLE
)) {
238 return NT_STATUS_NOT_SUPPORTED
;
241 ealist
= talloc(name
, struct xattr_DosEAs
);
243 /* load the current list */
244 status
= pvfs_doseas_load(pvfs
, name
, fd
, ealist
);
245 if (!NT_STATUS_IS_OK(status
)) {
249 for (j
=0;j
<num_eas
;j
++) {
250 struct ea_struct
*ea
= &eas
[j
];
251 /* see if its already there */
252 for (i
=0;i
<ealist
->num_eas
;i
++) {
253 if (strcasecmp_m(ealist
->eas
[i
].name
, ea
->name
.s
) == 0) {
254 ealist
->eas
[i
].value
= ea
->value
;
259 if (i
==ealist
->num_eas
) {
261 ealist
->eas
= talloc_realloc(ealist
, ealist
->eas
,
264 if (ealist
->eas
== NULL
) {
265 return NT_STATUS_NO_MEMORY
;
267 ealist
->eas
[i
].name
= ea
->name
.s
;
268 ealist
->eas
[i
].value
= ea
->value
;
273 /* pull out any null EAs */
274 for (i
=0;i
<ealist
->num_eas
;i
++) {
275 if (ealist
->eas
[i
].value
.length
== 0) {
276 memmove(&ealist
->eas
[i
],
278 (ealist
->num_eas
-(i
+1)) * sizeof(ealist
->eas
[i
]));
284 status
= pvfs_doseas_save(pvfs
, name
, fd
, ealist
);
285 if (!NT_STATUS_IS_OK(status
)) {
289 notify_trigger(pvfs
->notify_context
,
290 NOTIFY_ACTION_MODIFIED
,
291 FILE_NOTIFY_CHANGE_EA
,
294 name
->dos
.ea_size
= 4;
295 for (i
=0;i
<ealist
->num_eas
;i
++) {
296 name
->dos
.ea_size
+= 4 + strlen(ealist
->eas
[i
].name
)+1 +
297 ealist
->eas
[i
].value
.length
;
300 /* update the ea_size attrib */
301 return pvfs_dosattrib_save(pvfs
, name
, fd
);
305 set info on a open file
307 NTSTATUS
pvfs_setfileinfo(struct ntvfs_module_context
*ntvfs
,
308 struct ntvfs_request
*req
,
309 union smb_setfileinfo
*info
)
311 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
314 struct pvfs_file_handle
*h
;
315 struct pvfs_filename newstats
;
317 uint32_t access_needed
;
318 uint32_t change_mask
= 0;
320 f
= pvfs_find_fd(pvfs
, req
, info
->generic
.in
.file
.ntvfs
);
322 return NT_STATUS_INVALID_HANDLE
;
327 access_needed
= pvfs_setfileinfo_access(info
);
328 if ((f
->access_mask
& access_needed
) != access_needed
) {
329 return NT_STATUS_ACCESS_DENIED
;
332 /* update the file information */
333 status
= pvfs_resolve_name_handle(pvfs
, h
);
334 if (!NT_STATUS_IS_OK(status
)) {
338 /* we take a copy of the current file stats, then update
339 newstats in each of the elements below. At the end we
340 compare, and make any changes needed */
343 switch (info
->generic
.level
) {
344 case RAW_SFILEINFO_SETATTR
:
345 if (!null_time(info
->setattr
.in
.write_time
)) {
346 unix_to_nt_time(&newstats
.dos
.write_time
, info
->setattr
.in
.write_time
);
348 if (info
->setattr
.in
.attrib
!= FILE_ATTRIBUTE_NORMAL
) {
349 newstats
.dos
.attrib
= info
->setattr
.in
.attrib
;
353 case RAW_SFILEINFO_SETATTRE
:
354 case RAW_SFILEINFO_STANDARD
:
355 if (!null_time(info
->setattre
.in
.create_time
)) {
356 unix_to_nt_time(&newstats
.dos
.create_time
, info
->setattre
.in
.create_time
);
358 if (!null_time(info
->setattre
.in
.access_time
)) {
359 unix_to_nt_time(&newstats
.dos
.access_time
, info
->setattre
.in
.access_time
);
361 if (!null_time(info
->setattre
.in
.write_time
)) {
362 unix_to_nt_time(&newstats
.dos
.write_time
, info
->setattre
.in
.write_time
);
366 case RAW_SFILEINFO_EA_SET
:
367 return pvfs_setfileinfo_ea_set(pvfs
, h
->name
, h
->fd
,
368 info
->ea_set
.in
.num_eas
,
369 info
->ea_set
.in
.eas
);
371 case RAW_SFILEINFO_BASIC_INFO
:
372 case RAW_SFILEINFO_BASIC_INFORMATION
:
373 if (!null_nttime(info
->basic_info
.in
.create_time
)) {
374 newstats
.dos
.create_time
= info
->basic_info
.in
.create_time
;
376 if (!null_nttime(info
->basic_info
.in
.access_time
)) {
377 newstats
.dos
.access_time
= info
->basic_info
.in
.access_time
;
379 if (!null_nttime(info
->basic_info
.in
.write_time
)) {
380 newstats
.dos
.write_time
= info
->basic_info
.in
.write_time
;
382 if (!null_nttime(info
->basic_info
.in
.change_time
)) {
383 newstats
.dos
.change_time
= info
->basic_info
.in
.change_time
;
385 if (info
->basic_info
.in
.attrib
!= 0) {
386 newstats
.dos
.attrib
= info
->basic_info
.in
.attrib
;
390 case RAW_SFILEINFO_DISPOSITION_INFO
:
391 case RAW_SFILEINFO_DISPOSITION_INFORMATION
:
392 return pvfs_set_delete_on_close(pvfs
, req
, f
,
393 info
->disposition_info
.in
.delete_on_close
);
395 case RAW_SFILEINFO_ALLOCATION_INFO
:
396 case RAW_SFILEINFO_ALLOCATION_INFORMATION
:
397 status
= pvfs_break_level2_oplocks(f
);
398 NT_STATUS_NOT_OK_RETURN(status
);
400 newstats
.dos
.alloc_size
= info
->allocation_info
.in
.alloc_size
;
401 if (newstats
.dos
.alloc_size
< newstats
.st
.st_size
) {
402 newstats
.st
.st_size
= newstats
.dos
.alloc_size
;
404 newstats
.dos
.alloc_size
= pvfs_round_alloc_size(pvfs
,
405 newstats
.dos
.alloc_size
);
408 case RAW_SFILEINFO_END_OF_FILE_INFO
:
409 case RAW_SFILEINFO_END_OF_FILE_INFORMATION
:
410 status
= pvfs_break_level2_oplocks(f
);
411 NT_STATUS_NOT_OK_RETURN(status
);
413 newstats
.st
.st_size
= info
->end_of_file_info
.in
.size
;
416 case RAW_SFILEINFO_POSITION_INFORMATION
:
417 h
->position
= info
->position_information
.in
.position
;
420 case RAW_SFILEINFO_MODE_INFORMATION
:
421 /* this one is a puzzle */
422 if (info
->mode_information
.in
.mode
!= 0 &&
423 info
->mode_information
.in
.mode
!= 2 &&
424 info
->mode_information
.in
.mode
!= 4 &&
425 info
->mode_information
.in
.mode
!= 6) {
426 return NT_STATUS_INVALID_PARAMETER
;
428 h
->mode
= info
->mode_information
.in
.mode
;
431 case RAW_SFILEINFO_RENAME_INFORMATION
:
432 case RAW_SFILEINFO_RENAME_INFORMATION_SMB2
:
433 return pvfs_setfileinfo_rename(pvfs
, req
, h
->name
, f
->handle
->fd
,
437 case RAW_SFILEINFO_SEC_DESC
:
438 notify_trigger(pvfs
->notify_context
,
439 NOTIFY_ACTION_MODIFIED
,
440 FILE_NOTIFY_CHANGE_SECURITY
,
442 return pvfs_acl_set(pvfs
, req
, h
->name
, h
->fd
, f
->access_mask
, info
);
445 return NT_STATUS_INVALID_LEVEL
;
448 /* possibly change the file size */
449 if (newstats
.st
.st_size
!= h
->name
->st
.st_size
) {
450 if (h
->name
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
) {
451 return NT_STATUS_FILE_IS_A_DIRECTORY
;
453 if (h
->name
->stream_name
) {
454 status
= pvfs_stream_truncate(pvfs
, h
->name
, h
->fd
, newstats
.st
.st_size
);
455 if (!NT_STATUS_IS_OK(status
)) {
459 change_mask
|= FILE_NOTIFY_CHANGE_STREAM_SIZE
;
463 (SEC_FILE_WRITE_DATA
|SEC_FILE_APPEND_DATA
)) {
464 ret
= ftruncate(h
->fd
, newstats
.st
.st_size
);
466 ret
= truncate(h
->name
->full_name
, newstats
.st
.st_size
);
469 return pvfs_map_errno(pvfs
, errno
);
471 change_mask
|= FILE_NOTIFY_CHANGE_SIZE
| FILE_NOTIFY_CHANGE_ATTRIBUTES
;
475 /* possibly change the file timestamps */
476 if (newstats
.dos
.create_time
!= h
->name
->dos
.create_time
) {
477 change_mask
|= FILE_NOTIFY_CHANGE_CREATION
;
479 if (newstats
.dos
.access_time
!= h
->name
->dos
.access_time
) {
480 change_mask
|= FILE_NOTIFY_CHANGE_LAST_ACCESS
;
482 if (newstats
.dos
.write_time
!= h
->name
->dos
.write_time
) {
483 change_mask
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
485 if ((change_mask
& FILE_NOTIFY_CHANGE_LAST_ACCESS
) ||
486 (change_mask
& FILE_NOTIFY_CHANGE_LAST_WRITE
)) {
487 struct timeval tv
[2];
489 nttime_to_timeval(&tv
[0], newstats
.dos
.access_time
);
490 nttime_to_timeval(&tv
[1], newstats
.dos
.write_time
);
492 if (!timeval_is_zero(&tv
[0]) || !timeval_is_zero(&tv
[1])) {
493 if (utimes(h
->name
->full_name
, tv
) == -1) {
494 DEBUG(0,("pvfs_setfileinfo: utimes() failed '%s' - %s\n",
495 h
->name
->full_name
, strerror(errno
)));
496 return pvfs_map_errno(pvfs
, errno
);
500 if (change_mask
& FILE_NOTIFY_CHANGE_LAST_WRITE
) {
501 struct odb_lock
*lck
;
503 lck
= odb_lock(req
, h
->pvfs
->odb_context
, &h
->odb_locking_key
);
505 DEBUG(0,("Unable to lock opendb for write time update\n"));
506 return NT_STATUS_INTERNAL_ERROR
;
509 status
= odb_set_write_time(lck
, newstats
.dos
.write_time
, true);
510 if (!NT_STATUS_IS_OK(status
)) {
511 DEBUG(0,("Unable to update write time: %s\n",
519 h
->write_time
.update_forced
= true;
520 h
->write_time
.update_on_close
= false;
521 talloc_free(h
->write_time
.update_event
);
522 h
->write_time
.update_event
= NULL
;
525 /* possibly change the attribute */
526 if (newstats
.dos
.attrib
!= h
->name
->dos
.attrib
) {
528 if ((newstats
.dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
) &&
529 !(h
->name
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
)) {
530 return NT_STATUS_INVALID_PARAMETER
;
532 mode
= pvfs_fileperms(pvfs
, newstats
.dos
.attrib
);
533 if (!(h
->name
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
)) {
534 if (fchmod(h
->fd
, mode
) == -1) {
535 return pvfs_map_errno(pvfs
, errno
);
538 change_mask
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
543 notify_trigger(pvfs
->notify_context
,
544 NOTIFY_ACTION_MODIFIED
,
548 return pvfs_dosattrib_save(pvfs
, h
->name
, h
->fd
);
552 retry an open after a sharing violation
554 static void pvfs_retry_setpathinfo(struct pvfs_odb_retry
*r
,
555 struct ntvfs_module_context
*ntvfs
,
556 struct ntvfs_request
*req
,
559 enum pvfs_wait_notice reason
)
561 union smb_setfileinfo
*info
= talloc_get_type(_info
,
562 union smb_setfileinfo
);
563 NTSTATUS status
= NT_STATUS_INTERNAL_ERROR
;
568 case PVFS_WAIT_CANCEL
:
570 status
= NT_STATUS_CANCELLED
;
572 case PVFS_WAIT_TIMEOUT
:
573 /* if it timed out, then give the failure
576 status
= NT_STATUS_SHARING_VIOLATION
;
578 case PVFS_WAIT_EVENT
:
580 /* try the open again, which could trigger another retry setup
581 if it wants to, so we have to unmark the async flag so we
582 will know if it does a second async reply */
583 req
->async_states
->state
&= ~NTVFS_ASYNC_STATE_ASYNC
;
585 status
= pvfs_setpathinfo(ntvfs
, req
, info
);
586 if (req
->async_states
->state
& NTVFS_ASYNC_STATE_ASYNC
) {
587 /* the 2nd try also replied async, so we don't send
592 /* re-mark it async, just in case someone up the chain does
594 req
->async_states
->state
|= NTVFS_ASYNC_STATE_ASYNC
;
598 /* send the reply up the chain */
599 req
->async_states
->status
= status
;
600 req
->async_states
->send_fn(req
);
604 setup for a unlink retry after a sharing violation
605 or a non granted oplock
607 static NTSTATUS
pvfs_setpathinfo_setup_retry(struct ntvfs_module_context
*ntvfs
,
608 struct ntvfs_request
*req
,
609 union smb_setfileinfo
*info
,
610 struct odb_lock
*lck
,
613 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
615 struct timeval end_time
;
617 if (NT_STATUS_EQUAL(status
, NT_STATUS_SHARING_VIOLATION
)) {
618 end_time
= timeval_add(&req
->statistics
.request_time
,
619 0, pvfs
->sharing_violation_delay
);
620 } else if (NT_STATUS_EQUAL(status
, NT_STATUS_OPLOCK_NOT_GRANTED
)) {
621 end_time
= timeval_add(&req
->statistics
.request_time
,
622 pvfs
->oplock_break_timeout
, 0);
624 return NT_STATUS_INTERNAL_ERROR
;
627 return pvfs_odb_retry_setup(ntvfs
, req
, lck
, end_time
, info
, NULL
,
628 pvfs_retry_setpathinfo
);
632 set info on a pathname
634 NTSTATUS
pvfs_setpathinfo(struct ntvfs_module_context
*ntvfs
,
635 struct ntvfs_request
*req
, union smb_setfileinfo
*info
)
637 struct pvfs_state
*pvfs
= talloc_get_type(ntvfs
->private_data
,
639 struct pvfs_filename
*name
;
640 struct pvfs_filename newstats
;
642 uint32_t access_needed
;
643 uint32_t change_mask
= 0;
644 struct odb_lock
*lck
= NULL
;
645 DATA_BLOB odb_locking_key
;
647 /* resolve the cifs name to a posix name */
648 status
= pvfs_resolve_name(pvfs
, req
, info
->generic
.in
.file
.path
,
649 PVFS_RESOLVE_STREAMS
, &name
);
650 if (!NT_STATUS_IS_OK(status
)) {
655 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
658 access_needed
= pvfs_setfileinfo_access(info
);
659 status
= pvfs_access_check_simple(pvfs
, req
, name
, access_needed
);
660 if (!NT_STATUS_IS_OK(status
)) {
664 /* we take a copy of the current file stats, then update
665 newstats in each of the elements below. At the end we
666 compare, and make any changes needed */
669 switch (info
->generic
.level
) {
670 case RAW_SFILEINFO_SETATTR
:
671 if (!null_time(info
->setattr
.in
.write_time
)) {
672 unix_to_nt_time(&newstats
.dos
.write_time
, info
->setattr
.in
.write_time
);
674 if (info
->setattr
.in
.attrib
== 0) {
675 newstats
.dos
.attrib
= FILE_ATTRIBUTE_NORMAL
;
676 } else if (info
->setattr
.in
.attrib
!= FILE_ATTRIBUTE_NORMAL
) {
677 newstats
.dos
.attrib
= info
->setattr
.in
.attrib
;
681 case RAW_SFILEINFO_SETATTRE
:
682 case RAW_SFILEINFO_STANDARD
:
683 if (!null_time(info
->setattre
.in
.create_time
)) {
684 unix_to_nt_time(&newstats
.dos
.create_time
, info
->setattre
.in
.create_time
);
686 if (!null_time(info
->setattre
.in
.access_time
)) {
687 unix_to_nt_time(&newstats
.dos
.access_time
, info
->setattre
.in
.access_time
);
689 if (!null_time(info
->setattre
.in
.write_time
)) {
690 unix_to_nt_time(&newstats
.dos
.write_time
, info
->setattre
.in
.write_time
);
694 case RAW_SFILEINFO_EA_SET
:
695 return pvfs_setfileinfo_ea_set(pvfs
, name
, -1,
696 info
->ea_set
.in
.num_eas
,
697 info
->ea_set
.in
.eas
);
699 case RAW_SFILEINFO_BASIC_INFO
:
700 case RAW_SFILEINFO_BASIC_INFORMATION
:
701 if (!null_nttime(info
->basic_info
.in
.create_time
)) {
702 newstats
.dos
.create_time
= info
->basic_info
.in
.create_time
;
704 if (!null_nttime(info
->basic_info
.in
.access_time
)) {
705 newstats
.dos
.access_time
= info
->basic_info
.in
.access_time
;
707 if (!null_nttime(info
->basic_info
.in
.write_time
)) {
708 newstats
.dos
.write_time
= info
->basic_info
.in
.write_time
;
710 if (!null_nttime(info
->basic_info
.in
.change_time
)) {
711 newstats
.dos
.change_time
= info
->basic_info
.in
.change_time
;
713 if (info
->basic_info
.in
.attrib
!= 0) {
714 newstats
.dos
.attrib
= info
->basic_info
.in
.attrib
;
718 case RAW_SFILEINFO_ALLOCATION_INFO
:
719 case RAW_SFILEINFO_ALLOCATION_INFORMATION
:
720 status
= pvfs_can_update_file_size(pvfs
, req
, name
, &lck
);
722 * on a sharing violation we need to retry when the file is closed by
723 * the other user, or after 1 second
724 * on a non granted oplock we need to retry when the file is closed by
725 * the other user, or after 30 seconds
727 if ((NT_STATUS_EQUAL(status
, NT_STATUS_SHARING_VIOLATION
) ||
728 NT_STATUS_EQUAL(status
, NT_STATUS_OPLOCK_NOT_GRANTED
)) &&
729 (req
->async_states
->state
& NTVFS_ASYNC_STATE_MAY_ASYNC
)) {
730 return pvfs_setpathinfo_setup_retry(pvfs
->ntvfs
, req
, info
, lck
, status
);
732 NT_STATUS_NOT_OK_RETURN(status
);
734 if (info
->allocation_info
.in
.alloc_size
> newstats
.dos
.alloc_size
) {
735 /* strange. Increasing the allocation size via setpathinfo
736 should be silently ignored */
739 newstats
.dos
.alloc_size
= info
->allocation_info
.in
.alloc_size
;
740 if (newstats
.dos
.alloc_size
< newstats
.st
.st_size
) {
741 newstats
.st
.st_size
= newstats
.dos
.alloc_size
;
743 newstats
.dos
.alloc_size
= pvfs_round_alloc_size(pvfs
,
744 newstats
.dos
.alloc_size
);
747 case RAW_SFILEINFO_END_OF_FILE_INFO
:
748 case RAW_SFILEINFO_END_OF_FILE_INFORMATION
:
749 status
= pvfs_can_update_file_size(pvfs
, req
, name
, &lck
);
751 * on a sharing violation we need to retry when the file is closed by
752 * the other user, or after 1 second
753 * on a non granted oplock we need to retry when the file is closed by
754 * the other user, or after 30 seconds
756 if ((NT_STATUS_EQUAL(status
, NT_STATUS_SHARING_VIOLATION
) ||
757 NT_STATUS_EQUAL(status
, NT_STATUS_OPLOCK_NOT_GRANTED
)) &&
758 (req
->async_states
->state
& NTVFS_ASYNC_STATE_MAY_ASYNC
)) {
759 return pvfs_setpathinfo_setup_retry(pvfs
->ntvfs
, req
, info
, lck
, status
);
761 NT_STATUS_NOT_OK_RETURN(status
);
763 newstats
.st
.st_size
= info
->end_of_file_info
.in
.size
;
766 case RAW_SFILEINFO_MODE_INFORMATION
:
767 if (info
->mode_information
.in
.mode
!= 0 &&
768 info
->mode_information
.in
.mode
!= 2 &&
769 info
->mode_information
.in
.mode
!= 4 &&
770 info
->mode_information
.in
.mode
!= 6) {
771 return NT_STATUS_INVALID_PARAMETER
;
775 case RAW_SFILEINFO_RENAME_INFORMATION
:
776 case RAW_SFILEINFO_RENAME_INFORMATION_SMB2
:
777 status
= pvfs_locking_key(name
, name
, &odb_locking_key
);
778 NT_STATUS_NOT_OK_RETURN(status
);
779 status
= pvfs_setfileinfo_rename(pvfs
, req
, name
, -1,
780 &odb_locking_key
, info
);
781 NT_STATUS_NOT_OK_RETURN(status
);
784 case RAW_SFILEINFO_DISPOSITION_INFO
:
785 case RAW_SFILEINFO_DISPOSITION_INFORMATION
:
786 case RAW_SFILEINFO_POSITION_INFORMATION
:
790 return NT_STATUS_INVALID_LEVEL
;
793 /* possibly change the file size */
794 if (newstats
.st
.st_size
!= name
->st
.st_size
) {
795 if (name
->stream_name
) {
796 status
= pvfs_stream_truncate(pvfs
, name
, -1, newstats
.st
.st_size
);
797 if (!NT_STATUS_IS_OK(status
)) {
800 } else if (truncate(name
->full_name
, newstats
.st
.st_size
) == -1) {
801 return pvfs_map_errno(pvfs
, errno
);
803 change_mask
|= FILE_NOTIFY_CHANGE_SIZE
| FILE_NOTIFY_CHANGE_ATTRIBUTES
;
806 /* possibly change the file timestamps */
807 if (newstats
.dos
.create_time
!= name
->dos
.create_time
) {
808 change_mask
|= FILE_NOTIFY_CHANGE_CREATION
;
810 if (newstats
.dos
.access_time
!= name
->dos
.access_time
) {
811 change_mask
|= FILE_NOTIFY_CHANGE_LAST_ACCESS
;
813 if (newstats
.dos
.write_time
!= name
->dos
.write_time
) {
814 change_mask
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
816 if ((change_mask
& FILE_NOTIFY_CHANGE_LAST_ACCESS
) ||
817 (change_mask
& FILE_NOTIFY_CHANGE_LAST_WRITE
)) {
818 struct timeval tv
[2];
820 nttime_to_timeval(&tv
[0], newstats
.dos
.access_time
);
821 nttime_to_timeval(&tv
[1], newstats
.dos
.write_time
);
823 if (!timeval_is_zero(&tv
[0]) || !timeval_is_zero(&tv
[1])) {
824 if (utimes(name
->full_name
, tv
) == -1) {
825 DEBUG(0,("pvfs_setpathinfo: utimes() failed '%s' - %s\n",
826 name
->full_name
, strerror(errno
)));
827 return pvfs_map_errno(pvfs
, errno
);
831 if (change_mask
& FILE_NOTIFY_CHANGE_LAST_WRITE
) {
834 status
= pvfs_locking_key(name
, name
, &lkey
);
835 NT_STATUS_NOT_OK_RETURN(status
);
837 lck
= odb_lock(req
, pvfs
->odb_context
, &lkey
);
838 data_blob_free(&lkey
);
840 DEBUG(0,("Unable to lock opendb for write time update\n"));
841 return NT_STATUS_INTERNAL_ERROR
;
845 status
= odb_set_write_time(lck
, newstats
.dos
.write_time
, true);
846 if (NT_STATUS_EQUAL(status
, NT_STATUS_OBJECT_NAME_NOT_FOUND
)) {
847 /* it could be that nobody has opened the file */
848 } else if (!NT_STATUS_IS_OK(status
)) {
849 DEBUG(0,("Unable to update write time: %s\n",
855 /* possibly change the attribute */
856 newstats
.dos
.attrib
|= (name
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
);
857 if (newstats
.dos
.attrib
!= name
->dos
.attrib
) {
858 mode_t mode
= pvfs_fileperms(pvfs
, newstats
.dos
.attrib
);
859 if (chmod(name
->full_name
, mode
) == -1) {
860 return pvfs_map_errno(pvfs
, errno
);
862 change_mask
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
867 if (change_mask
!= 0) {
868 notify_trigger(pvfs
->notify_context
,
869 NOTIFY_ACTION_MODIFIED
,
874 return pvfs_dosattrib_save(pvfs
, name
, -1);