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 2 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, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #include "vfs_posix.h"
25 #include "system/time.h"
26 #include "librpc/gen_ndr/ndr_xattr.h"
30 determine what access bits are needed for a call
32 static uint32_t pvfs_setfileinfo_access(union smb_setfileinfo
*info
)
36 switch (info
->generic
.level
) {
37 case RAW_SFILEINFO_EA_SET
:
38 needed
= SEC_FILE_WRITE_EA
;
41 case RAW_SFILEINFO_DISPOSITION_INFO
:
42 case RAW_SFILEINFO_DISPOSITION_INFORMATION
:
43 needed
= SEC_STD_DELETE
;
46 case RAW_SFILEINFO_END_OF_FILE_INFO
:
47 needed
= SEC_FILE_WRITE_DATA
;
50 case RAW_SFILEINFO_POSITION_INFORMATION
:
54 case RAW_SFILEINFO_SEC_DESC
:
56 if (info
->set_secdesc
.in
.secinfo_flags
& (SECINFO_DACL
|SECINFO_SACL
)) {
57 needed
|= SEC_STD_WRITE_DAC
;
62 needed
= SEC_FILE_WRITE_ATTRIBUTE
;
69 rename_information level
71 static NTSTATUS
pvfs_setfileinfo_rename(struct pvfs_state
*pvfs
,
72 struct smbsrv_request
*req
,
73 struct pvfs_filename
*name
,
74 struct smb_rename_information
*r
)
77 struct pvfs_filename
*name2
;
80 /* renames are only allowed within a directory */
81 if (strchr_m(r
->new_name
, '\\')) {
82 return NT_STATUS_NOT_SUPPORTED
;
85 if (name
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
) {
86 /* don't allow this for now */
87 return NT_STATUS_FILE_IS_A_DIRECTORY
;
90 /* don't allow stream renames for now */
91 if (name
->stream_name
) {
92 return NT_STATUS_INVALID_PARAMETER
;
95 /* w2k3 does not appear to allow relative rename */
96 if (r
->root_fid
!= 0) {
97 return NT_STATUS_INVALID_PARAMETER
;
100 /* construct the fully qualified windows name for the new file name */
101 new_name
= talloc_strdup(req
, name
->original_name
);
102 if (new_name
== NULL
) {
103 return NT_STATUS_NO_MEMORY
;
105 p
= strrchr_m(new_name
, '\\');
107 return NT_STATUS_OBJECT_NAME_INVALID
;
111 new_name
= talloc_asprintf(req
, "%s\\%s", new_name
, r
->new_name
);
112 if (new_name
== NULL
) {
113 return NT_STATUS_NO_MEMORY
;
116 /* resolve the new name */
117 status
= pvfs_resolve_name(pvfs
, name
, new_name
, 0, &name2
);
118 if (!NT_STATUS_IS_OK(status
)) {
122 /* if the destination exists, then check the rename is allowed */
124 if (strcmp(name2
->full_name
, name
->full_name
) == 0) {
125 /* rename to same name is null-op */
130 return NT_STATUS_OBJECT_NAME_COLLISION
;
133 status
= pvfs_can_delete(pvfs
, req
, name2
);
134 if (NT_STATUS_EQUAL(status
, NT_STATUS_SHARING_VIOLATION
)) {
135 return NT_STATUS_ACCESS_DENIED
;
137 if (!NT_STATUS_IS_OK(status
)) {
142 status
= pvfs_access_check_parent(pvfs
, req
, name2
, SEC_DIR_ADD_FILE
);
143 if (!NT_STATUS_IS_OK(status
)) {
147 if (rename(name
->full_name
, name2
->full_name
) == -1) {
148 return pvfs_map_errno(pvfs
, errno
);
151 name
->full_name
= talloc_steal(name
, name2
->full_name
);
152 name
->original_name
= talloc_steal(name
, name2
->original_name
);
160 NTSTATUS
pvfs_setfileinfo_ea_set(struct pvfs_state
*pvfs
,
161 struct pvfs_filename
*name
,
162 int fd
, uint16_t num_eas
,
163 struct ea_struct
*eas
)
165 struct xattr_DosEAs
*ealist
;
173 if (!(pvfs
->flags
& PVFS_FLAG_XATTR_ENABLE
)) {
174 return NT_STATUS_NOT_SUPPORTED
;
177 ealist
= talloc(name
, struct xattr_DosEAs
);
179 /* load the current list */
180 status
= pvfs_doseas_load(pvfs
, name
, fd
, ealist
);
181 if (!NT_STATUS_IS_OK(status
)) {
185 for (j
=0;j
<num_eas
;j
++) {
186 struct ea_struct
*ea
= &eas
[j
];
187 /* see if its already there */
188 for (i
=0;i
<ealist
->num_eas
;i
++) {
189 if (StrCaseCmp(ealist
->eas
[i
].name
, ea
->name
.s
) == 0) {
190 ealist
->eas
[i
].value
= ea
->value
;
195 if (i
==ealist
->num_eas
) {
197 ealist
->eas
= talloc_realloc(ealist
, ealist
->eas
,
200 if (ealist
->eas
== NULL
) {
201 return NT_STATUS_NO_MEMORY
;
203 ealist
->eas
[i
].name
= ea
->name
.s
;
204 ealist
->eas
[i
].value
= ea
->value
;
209 /* pull out any null EAs */
210 for (i
=0;i
<ealist
->num_eas
;i
++) {
211 if (ealist
->eas
[i
].value
.length
== 0) {
212 memmove(&ealist
->eas
[i
],
214 (ealist
->num_eas
-(i
+1)) * sizeof(ealist
->eas
[i
]));
220 status
= pvfs_doseas_save(pvfs
, name
, fd
, ealist
);
221 if (!NT_STATUS_IS_OK(status
)) {
225 name
->dos
.ea_size
= 4;
226 for (i
=0;i
<ealist
->num_eas
;i
++) {
227 name
->dos
.ea_size
+= 4 + strlen(ealist
->eas
[i
].name
)+1 +
228 ealist
->eas
[i
].value
.length
;
231 /* update the ea_size attrib */
232 return pvfs_dosattrib_save(pvfs
, name
, fd
);
236 set info on a open file
238 NTSTATUS
pvfs_setfileinfo(struct ntvfs_module_context
*ntvfs
,
239 struct smbsrv_request
*req
,
240 union smb_setfileinfo
*info
)
242 struct pvfs_state
*pvfs
= ntvfs
->private_data
;
243 struct utimbuf unix_times
;
245 struct pvfs_file_handle
*h
;
246 uint32_t create_options
;
247 struct pvfs_filename newstats
;
249 uint32_t access_needed
;
251 f
= pvfs_find_fd(pvfs
, req
, info
->generic
.file
.fnum
);
253 return NT_STATUS_INVALID_HANDLE
;
258 access_needed
= pvfs_setfileinfo_access(info
);
259 if ((f
->access_mask
& access_needed
) != access_needed
) {
260 return NT_STATUS_ACCESS_DENIED
;
263 /* update the file information */
264 status
= pvfs_resolve_name_fd(pvfs
, h
->fd
, h
->name
);
265 if (!NT_STATUS_IS_OK(status
)) {
269 /* we take a copy of the current file stats, then update
270 newstats in each of the elements below. At the end we
271 compare, and make any changes needed */
274 switch (info
->generic
.level
) {
275 case RAW_SFILEINFO_SETATTR
:
276 if (!null_time(info
->setattr
.in
.write_time
)) {
277 unix_to_nt_time(&newstats
.dos
.write_time
, info
->setattr
.in
.write_time
);
279 if (info
->setattr
.in
.attrib
!= FILE_ATTRIBUTE_NORMAL
) {
280 newstats
.dos
.attrib
= info
->setattr
.in
.attrib
;
284 case RAW_SFILEINFO_SETATTRE
:
285 case RAW_SFILEINFO_STANDARD
:
286 if (!null_time(info
->setattre
.in
.create_time
)) {
287 unix_to_nt_time(&newstats
.dos
.create_time
, info
->setattre
.in
.create_time
);
289 if (!null_time(info
->setattre
.in
.access_time
)) {
290 unix_to_nt_time(&newstats
.dos
.access_time
, info
->setattre
.in
.access_time
);
292 if (!null_time(info
->setattre
.in
.write_time
)) {
293 unix_to_nt_time(&newstats
.dos
.write_time
, info
->setattre
.in
.write_time
);
297 case RAW_SFILEINFO_EA_SET
:
298 return pvfs_setfileinfo_ea_set(pvfs
, h
->name
, h
->fd
,
299 info
->ea_set
.in
.num_eas
,
300 info
->ea_set
.in
.eas
);
302 case RAW_SFILEINFO_BASIC_INFO
:
303 case RAW_SFILEINFO_BASIC_INFORMATION
:
304 if (!null_nttime(info
->basic_info
.in
.create_time
)) {
305 newstats
.dos
.create_time
= info
->basic_info
.in
.create_time
;
307 if (!null_nttime(info
->basic_info
.in
.access_time
)) {
308 newstats
.dos
.access_time
= info
->basic_info
.in
.access_time
;
310 if (!null_nttime(info
->basic_info
.in
.write_time
)) {
311 newstats
.dos
.write_time
= info
->basic_info
.in
.write_time
;
312 newstats
.dos
.flags
|= XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME
;
313 h
->sticky_write_time
= True
;
315 if (!null_nttime(info
->basic_info
.in
.change_time
)) {
316 newstats
.dos
.change_time
= info
->basic_info
.in
.change_time
;
318 if (info
->basic_info
.in
.attrib
!= 0) {
319 newstats
.dos
.attrib
= info
->basic_info
.in
.attrib
;
323 case RAW_SFILEINFO_DISPOSITION_INFO
:
324 case RAW_SFILEINFO_DISPOSITION_INFORMATION
:
325 create_options
= h
->create_options
;
326 if (info
->disposition_info
.in
.delete_on_close
) {
327 create_options
|= NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
329 create_options
&= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE
;
331 return pvfs_change_create_options(pvfs
, req
, f
, create_options
);
333 case RAW_SFILEINFO_ALLOCATION_INFO
:
334 case RAW_SFILEINFO_ALLOCATION_INFORMATION
:
335 newstats
.dos
.alloc_size
= info
->allocation_info
.in
.alloc_size
;
336 if (newstats
.dos
.alloc_size
< newstats
.st
.st_size
) {
337 newstats
.st
.st_size
= newstats
.dos
.alloc_size
;
339 newstats
.dos
.alloc_size
= pvfs_round_alloc_size(pvfs
,
340 newstats
.dos
.alloc_size
);
343 case RAW_SFILEINFO_END_OF_FILE_INFO
:
344 case RAW_SFILEINFO_END_OF_FILE_INFORMATION
:
345 newstats
.st
.st_size
= info
->end_of_file_info
.in
.size
;
348 case RAW_SFILEINFO_POSITION_INFORMATION
:
349 h
->position
= info
->position_information
.in
.position
;
352 case RAW_SFILEINFO_MODE_INFORMATION
:
353 /* this one is a puzzle */
354 if (info
->mode_information
.in
.mode
!= 0 &&
355 info
->mode_information
.in
.mode
!= 2 &&
356 info
->mode_information
.in
.mode
!= 4 &&
357 info
->mode_information
.in
.mode
!= 6) {
358 return NT_STATUS_INVALID_PARAMETER
;
360 h
->mode
= info
->mode_information
.in
.mode
;
363 case RAW_SFILEINFO_RENAME_INFORMATION
:
364 return pvfs_setfileinfo_rename(pvfs
, req
, h
->name
,
365 &info
->rename_information
.in
);
367 case RAW_SFILEINFO_SEC_DESC
:
368 return pvfs_acl_set(pvfs
, req
, h
->name
, h
->fd
, f
->access_mask
, info
);
371 return NT_STATUS_INVALID_LEVEL
;
374 /* possibly change the file size */
375 if (newstats
.st
.st_size
!= h
->name
->st
.st_size
) {
376 if (h
->name
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
) {
377 return NT_STATUS_FILE_IS_A_DIRECTORY
;
379 if (h
->name
->stream_name
) {
380 status
= pvfs_stream_truncate(pvfs
, h
->name
, h
->fd
, newstats
.st
.st_size
);
381 if (!NT_STATUS_IS_OK(status
)) {
387 (SEC_FILE_WRITE_DATA
|SEC_FILE_APPEND_DATA
)) {
388 ret
= ftruncate(h
->fd
, newstats
.st
.st_size
);
390 ret
= truncate(h
->name
->full_name
, newstats
.st
.st_size
);
393 return pvfs_map_errno(pvfs
, errno
);
398 /* possibly change the file timestamps */
399 ZERO_STRUCT(unix_times
);
400 if (newstats
.dos
.access_time
!= h
->name
->dos
.access_time
) {
401 unix_times
.actime
= nt_time_to_unix(newstats
.dos
.access_time
);
403 if (newstats
.dos
.write_time
!= h
->name
->dos
.write_time
) {
404 unix_times
.modtime
= nt_time_to_unix(newstats
.dos
.write_time
);
406 if (unix_times
.actime
!= 0 || unix_times
.modtime
!= 0) {
407 if (utime(h
->name
->full_name
, &unix_times
) == -1) {
408 return pvfs_map_errno(pvfs
, errno
);
412 /* possibly change the attribute */
413 if (newstats
.dos
.attrib
!= h
->name
->dos
.attrib
) {
414 mode_t mode
= pvfs_fileperms(pvfs
, newstats
.dos
.attrib
);
415 if (!(h
->name
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
)) {
416 if (fchmod(h
->fd
, mode
) == -1) {
417 return pvfs_map_errno(pvfs
, errno
);
424 return pvfs_dosattrib_save(pvfs
, h
->name
, h
->fd
);
429 set info on a pathname
431 NTSTATUS
pvfs_setpathinfo(struct ntvfs_module_context
*ntvfs
,
432 struct smbsrv_request
*req
, union smb_setfileinfo
*info
)
434 struct pvfs_state
*pvfs
= ntvfs
->private_data
;
435 struct pvfs_filename
*name
;
436 struct pvfs_filename newstats
;
438 struct utimbuf unix_times
;
439 uint32_t access_needed
;
441 /* resolve the cifs name to a posix name */
442 status
= pvfs_resolve_name(pvfs
, req
, info
->generic
.file
.fname
,
443 PVFS_RESOLVE_STREAMS
, &name
);
444 if (!NT_STATUS_IS_OK(status
)) {
449 return NT_STATUS_OBJECT_NAME_NOT_FOUND
;
452 access_needed
= pvfs_setfileinfo_access(info
);
453 status
= pvfs_access_check_simple(pvfs
, req
, name
, access_needed
);
454 if (!NT_STATUS_IS_OK(status
)) {
458 /* we take a copy of the current file stats, then update
459 newstats in each of the elements below. At the end we
460 compare, and make any changes needed */
463 switch (info
->generic
.level
) {
464 case RAW_SFILEINFO_SETATTR
:
465 if (!null_time(info
->setattr
.in
.write_time
)) {
466 unix_to_nt_time(&newstats
.dos
.write_time
, info
->setattr
.in
.write_time
);
468 if (info
->setattr
.in
.attrib
!= FILE_ATTRIBUTE_NORMAL
) {
469 newstats
.dos
.attrib
= info
->setattr
.in
.attrib
;
473 case RAW_SFILEINFO_SETATTRE
:
474 case RAW_SFILEINFO_STANDARD
:
475 if (!null_time(info
->setattre
.in
.create_time
)) {
476 unix_to_nt_time(&newstats
.dos
.create_time
, info
->setattre
.in
.create_time
);
478 if (!null_time(info
->setattre
.in
.access_time
)) {
479 unix_to_nt_time(&newstats
.dos
.access_time
, info
->setattre
.in
.access_time
);
481 if (!null_time(info
->setattre
.in
.write_time
)) {
482 unix_to_nt_time(&newstats
.dos
.write_time
, info
->setattre
.in
.write_time
);
486 case RAW_SFILEINFO_EA_SET
:
487 return pvfs_setfileinfo_ea_set(pvfs
, name
, -1,
488 info
->ea_set
.in
.num_eas
,
489 info
->ea_set
.in
.eas
);
491 case RAW_SFILEINFO_BASIC_INFO
:
492 case RAW_SFILEINFO_BASIC_INFORMATION
:
493 if (!null_nttime(info
->basic_info
.in
.create_time
)) {
494 newstats
.dos
.create_time
= info
->basic_info
.in
.create_time
;
496 if (!null_nttime(info
->basic_info
.in
.access_time
)) {
497 newstats
.dos
.access_time
= info
->basic_info
.in
.access_time
;
499 if (!null_nttime(info
->basic_info
.in
.write_time
)) {
500 newstats
.dos
.write_time
= info
->basic_info
.in
.write_time
;
502 if (!null_nttime(info
->basic_info
.in
.change_time
)) {
503 newstats
.dos
.change_time
= info
->basic_info
.in
.change_time
;
505 if (info
->basic_info
.in
.attrib
!= 0) {
506 newstats
.dos
.attrib
= info
->basic_info
.in
.attrib
;
510 case RAW_SFILEINFO_ALLOCATION_INFO
:
511 case RAW_SFILEINFO_ALLOCATION_INFORMATION
:
512 if (info
->allocation_info
.in
.alloc_size
> newstats
.dos
.alloc_size
) {
513 /* strange. Increasing the allocation size via setpathinfo
514 should be silently ignored */
517 newstats
.dos
.alloc_size
= info
->allocation_info
.in
.alloc_size
;
518 if (newstats
.dos
.alloc_size
< newstats
.st
.st_size
) {
519 newstats
.st
.st_size
= newstats
.dos
.alloc_size
;
521 newstats
.dos
.alloc_size
= pvfs_round_alloc_size(pvfs
,
522 newstats
.dos
.alloc_size
);
525 case RAW_SFILEINFO_END_OF_FILE_INFO
:
526 case RAW_SFILEINFO_END_OF_FILE_INFORMATION
:
527 newstats
.st
.st_size
= info
->end_of_file_info
.in
.size
;
530 case RAW_SFILEINFO_MODE_INFORMATION
:
531 if (info
->mode_information
.in
.mode
!= 0 &&
532 info
->mode_information
.in
.mode
!= 2 &&
533 info
->mode_information
.in
.mode
!= 4 &&
534 info
->mode_information
.in
.mode
!= 6) {
535 return NT_STATUS_INVALID_PARAMETER
;
539 case RAW_SFILEINFO_RENAME_INFORMATION
:
540 return pvfs_setfileinfo_rename(pvfs
, req
, name
,
541 &info
->rename_information
.in
);
543 case RAW_SFILEINFO_DISPOSITION_INFO
:
544 case RAW_SFILEINFO_DISPOSITION_INFORMATION
:
545 case RAW_SFILEINFO_POSITION_INFORMATION
:
549 return NT_STATUS_INVALID_LEVEL
;
552 /* possibly change the file size */
553 if (newstats
.st
.st_size
!= name
->st
.st_size
) {
554 if (name
->stream_name
) {
555 status
= pvfs_stream_truncate(pvfs
, name
, -1, newstats
.st
.st_size
);
556 if (!NT_STATUS_IS_OK(status
)) {
559 } else if (truncate(name
->full_name
, newstats
.st
.st_size
) == -1) {
560 return pvfs_map_errno(pvfs
, errno
);
564 /* possibly change the file timestamps */
565 ZERO_STRUCT(unix_times
);
566 if (newstats
.dos
.access_time
!= name
->dos
.access_time
) {
567 unix_times
.actime
= nt_time_to_unix(newstats
.dos
.access_time
);
569 if (newstats
.dos
.write_time
!= name
->dos
.write_time
) {
570 unix_times
.modtime
= nt_time_to_unix(newstats
.dos
.write_time
);
572 if (unix_times
.actime
!= 0 || unix_times
.modtime
!= 0) {
573 if (utime(name
->full_name
, &unix_times
) == -1) {
574 return pvfs_map_errno(pvfs
, errno
);
578 /* possibly change the attribute */
579 newstats
.dos
.attrib
|= (name
->dos
.attrib
& FILE_ATTRIBUTE_DIRECTORY
);
580 if (newstats
.dos
.attrib
!= name
->dos
.attrib
) {
581 mode_t mode
= pvfs_fileperms(pvfs
, newstats
.dos
.attrib
);
582 if (chmod(name
->full_name
, mode
) == -1) {
583 return pvfs_map_errno(pvfs
, errno
);
589 return pvfs_dosattrib_save(pvfs
, name
, -1);