s4:torture/smb2: remove allow_warnings=True
[Samba.git] / source3 / smbd / dosmode.c
blob8c0781b19ed830669f8bf528cb5242ca450ce782
1 /*
2 Unix SMB/CIFS implementation.
3 dos mode handling functions
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) James Peach 2006
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "librpc/gen_ndr/ndr_xattr.h"
24 #include "librpc/gen_ndr/ioctl.h"
25 #include "../libcli/security/security.h"
26 #include "smbd/smbd.h"
27 #include "lib/param/loadparm.h"
29 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
30 struct smb_filename *smb_fname,
31 files_struct **ret_fsp,
32 bool *need_close);
34 static void dos_mode_debug_print(uint32_t mode)
36 DEBUG(8,("dos_mode returning "));
38 if (mode & FILE_ATTRIBUTE_HIDDEN) {
39 DEBUG(8, ("h"));
41 if (mode & FILE_ATTRIBUTE_READONLY) {
42 DEBUG(8, ("r"));
44 if (mode & FILE_ATTRIBUTE_SYSTEM) {
45 DEBUG(8, ("s"));
47 if (mode & FILE_ATTRIBUTE_DIRECTORY) {
48 DEBUG(8, ("d"));
50 if (mode & FILE_ATTRIBUTE_ARCHIVE) {
51 DEBUG(8, ("a"));
53 if (mode & FILE_ATTRIBUTE_SPARSE) {
54 DEBUG(8, ("[sparse]"));
56 if (mode & FILE_ATTRIBUTE_OFFLINE) {
57 DEBUG(8, ("[offline]"));
59 if (mode & FILE_ATTRIBUTE_COMPRESSED) {
60 DEBUG(8, ("[compressed]"));
63 DEBUG(8,("\n"));
66 static uint32_t filter_mode_by_protocol(uint32_t mode)
68 if (get_Protocol() <= PROTOCOL_LANMAN2) {
69 DEBUG(10,("filter_mode_by_protocol: "
70 "filtering result 0x%x to 0x%x\n",
71 (unsigned int)mode,
72 (unsigned int)(mode & 0x3f) ));
73 mode &= 0x3f;
75 return mode;
78 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
80 #ifdef S_ISLNK
81 #if LINKS_READ_ONLY
82 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
83 return FILE_ATTRIBUTE_READONLY;
84 #endif
85 #endif
86 return 0;
89 /****************************************************************************
90 Change a dos mode to a unix mode.
91 Base permission for files:
92 if creating file and inheriting (i.e. parent_dir != NULL)
93 apply read/write bits from parent directory.
94 else
95 everybody gets read bit set
96 dos readonly is represented in unix by removing everyone's write bit
97 dos archive is represented in unix by the user's execute bit
98 dos system is represented in unix by the group's execute bit
99 dos hidden is represented in unix by the other's execute bit
100 if !inheriting {
101 Then apply create mask,
102 then add force bits.
104 Base permission for directories:
105 dos directory is represented in unix by unix's dir bit and the exec bit
106 if !inheriting {
107 Then apply create mask,
108 then add force bits.
110 ****************************************************************************/
112 mode_t unix_mode(connection_struct *conn, int dosmode,
113 const struct smb_filename *smb_fname,
114 const char *inherit_from_dir)
116 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
117 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
118 * inheriting. */
120 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
121 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
124 if ((inherit_from_dir != NULL) && lp_inherit_permissions(SNUM(conn))) {
125 struct smb_filename *smb_fname_parent;
127 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
128 smb_fname_str_dbg(smb_fname),
129 inherit_from_dir));
131 smb_fname_parent = synthetic_smb_fname(
132 talloc_tos(), inherit_from_dir, NULL, NULL);
133 if (smb_fname_parent == NULL) {
134 DEBUG(1,("unix_mode(%s) failed, [dir %s]: No memory\n",
135 smb_fname_str_dbg(smb_fname),
136 inherit_from_dir));
137 return(0);
140 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
141 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
142 smb_fname_str_dbg(smb_fname),
143 inherit_from_dir, strerror(errno)));
144 TALLOC_FREE(smb_fname_parent);
145 return(0); /* *** shouldn't happen! *** */
148 /* Save for later - but explicitly remove setuid bit for safety. */
149 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
150 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
151 smb_fname_str_dbg(smb_fname), (int)dir_mode));
152 /* Clear "result" */
153 result = 0;
154 TALLOC_FREE(smb_fname_parent);
157 if (IS_DOS_DIR(dosmode)) {
158 /* We never make directories read only for the owner as under DOS a user
159 can always create a file in a read-only directory. */
160 result |= (S_IFDIR | S_IWUSR);
162 if (dir_mode) {
163 /* Inherit mode of parent directory. */
164 result |= dir_mode;
165 } else {
166 /* Provisionally add all 'x' bits */
167 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
169 /* Apply directory mask */
170 result &= lp_directory_mask(SNUM(conn));
171 /* Add in force bits */
172 result |= lp_force_directory_mode(SNUM(conn));
174 } else {
175 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
176 result |= S_IXUSR;
178 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
179 result |= S_IXGRP;
181 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
182 result |= S_IXOTH;
184 if (dir_mode) {
185 /* Inherit 666 component of parent directory mode */
186 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
187 } else {
188 /* Apply mode mask */
189 result &= lp_create_mask(SNUM(conn));
190 /* Add in force bits */
191 result |= lp_force_create_mode(SNUM(conn));
195 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
196 (int)result));
197 return(result);
200 /****************************************************************************
201 Change a unix mode to a dos mode.
202 ****************************************************************************/
204 static uint32 dos_mode_from_sbuf(connection_struct *conn,
205 const struct smb_filename *smb_fname)
207 int result = 0;
208 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
210 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
211 /* if we can find out if a file is immutable we should report it r/o */
212 if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
213 result |= FILE_ATTRIBUTE_READONLY;
215 #endif
216 if (ro_opts == MAP_READONLY_YES) {
217 /* Original Samba method - map inverse of user "w" bit. */
218 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
219 result |= FILE_ATTRIBUTE_READONLY;
221 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
222 /* Check actual permissions for read-only. */
223 if (!can_write_to_file(conn, smb_fname)) {
224 result |= FILE_ATTRIBUTE_READONLY;
226 } /* Else never set the readonly bit. */
228 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
229 result |= FILE_ATTRIBUTE_ARCHIVE;
231 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
232 result |= FILE_ATTRIBUTE_SYSTEM;
234 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
235 result |= FILE_ATTRIBUTE_HIDDEN;
237 if (S_ISDIR(smb_fname->st.st_ex_mode))
238 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
240 result |= set_link_read_only_flag(&smb_fname->st);
242 DEBUG(8,("dos_mode_from_sbuf returning "));
244 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
245 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
246 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
247 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
248 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
250 DEBUG(8,("\n"));
251 return result;
254 /****************************************************************************
255 Get DOS attributes from an EA.
256 This can also pull the create time into the stat struct inside smb_fname.
257 ****************************************************************************/
259 static bool get_ea_dos_attribute(connection_struct *conn,
260 struct smb_filename *smb_fname,
261 uint32 *pattr)
263 struct xattr_DOSATTRIB dosattrib;
264 enum ndr_err_code ndr_err;
265 DATA_BLOB blob;
266 ssize_t sizeret;
267 fstring attrstr;
268 uint32_t dosattr;
270 if (!lp_store_dos_attributes(SNUM(conn))) {
271 return False;
274 /* Don't reset pattr to zero as we may already have filename-based attributes we
275 need to preserve. */
277 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
278 SAMBA_XATTR_DOS_ATTRIB, attrstr,
279 sizeof(attrstr));
280 if (sizeret == -1) {
281 if (errno == ENOSYS
282 #if defined(ENOTSUP)
283 || errno == ENOTSUP) {
284 #else
286 #endif
287 DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
288 "from EA on file %s: Error = %s\n",
289 smb_fname_str_dbg(smb_fname),
290 strerror(errno)));
291 set_store_dos_attributes(SNUM(conn), False);
293 return False;
296 blob.data = (uint8_t *)attrstr;
297 blob.length = sizeret;
299 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
300 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
302 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
303 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
304 "from EA on file %s: Error = %s\n",
305 smb_fname_str_dbg(smb_fname),
306 ndr_errstr(ndr_err)));
307 return false;
310 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
311 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
313 switch (dosattrib.version) {
314 case 0xFFFF:
315 dosattr = dosattrib.info.compatinfoFFFF.attrib;
316 break;
317 case 1:
318 dosattr = dosattrib.info.info1.attrib;
319 if (!null_nttime(dosattrib.info.info1.create_time)) {
320 struct timespec create_time =
321 nt_time_to_unix_timespec(
322 dosattrib.info.info1.create_time);
324 update_stat_ex_create_time(&smb_fname->st,
325 create_time);
327 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
328 "set btime %s\n",
329 smb_fname_str_dbg(smb_fname),
330 time_to_asc(convert_timespec_to_time_t(
331 create_time)) ));
333 break;
334 case 2:
335 dosattr = dosattrib.info.oldinfo2.attrib;
336 /* Don't know what flags to check for this case. */
337 break;
338 case 3:
339 dosattr = dosattrib.info.info3.attrib;
340 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
341 !null_nttime(dosattrib.info.info3.create_time)) {
342 struct timespec create_time =
343 nt_time_to_unix_timespec(
344 dosattrib.info.info3.create_time);
346 update_stat_ex_create_time(&smb_fname->st,
347 create_time);
349 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
350 "set btime %s\n",
351 smb_fname_str_dbg(smb_fname),
352 time_to_asc(convert_timespec_to_time_t(
353 create_time)) ));
355 break;
356 default:
357 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
358 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
359 attrstr));
360 return false;
363 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
364 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
366 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
367 *pattr = (uint32)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
369 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
371 if (dosattr & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
372 if (dosattr & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
373 if (dosattr & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
374 if (dosattr & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
375 if (dosattr & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
377 DEBUG(8,("\n"));
379 return True;
382 /****************************************************************************
383 Set DOS attributes in an EA.
384 Also sets the create time.
385 ****************************************************************************/
387 static bool set_ea_dos_attribute(connection_struct *conn,
388 struct smb_filename *smb_fname,
389 uint32 dosmode)
391 struct xattr_DOSATTRIB dosattrib;
392 enum ndr_err_code ndr_err;
393 DATA_BLOB blob;
395 ZERO_STRUCT(dosattrib);
396 ZERO_STRUCT(blob);
398 dosattrib.version = 3;
399 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
400 XATTR_DOSINFO_CREATE_TIME;
401 dosattrib.info.info3.attrib = dosmode;
402 dosattrib.info.info3.create_time = unix_timespec_to_nt_time(
403 smb_fname->st.st_ex_btime);
405 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
406 (unsigned int)dosmode,
407 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
408 smb_fname_str_dbg(smb_fname) ));
410 ndr_err = ndr_push_struct_blob(
411 &blob, talloc_tos(), &dosattrib,
412 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
414 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
415 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
416 ndr_errstr(ndr_err)));
417 return false;
420 if (blob.data == NULL || blob.length == 0) {
421 return false;
424 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
425 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
426 0) == -1) {
427 bool ret = false;
428 bool need_close = false;
429 files_struct *fsp = NULL;
431 if((errno != EPERM) && (errno != EACCES)) {
432 if (errno == ENOSYS
433 #if defined(ENOTSUP)
434 || errno == ENOTSUP) {
435 #else
437 #endif
438 DEBUG(1,("set_ea_dos_attributes: Cannot set "
439 "attribute EA on file %s: Error = %s\n",
440 smb_fname_str_dbg(smb_fname),
441 strerror(errno) ));
442 set_store_dos_attributes(SNUM(conn), False);
444 return false;
447 /* We want DOS semantics, ie allow non owner with write permission to change the
448 bits on a file. Just like file_ntimes below.
451 /* Check if we have write access. */
452 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
453 return false;
455 if (!can_write_to_file(conn, smb_fname)) {
456 return false;
460 * We need to get an open file handle to do the
461 * metadata operation under root.
464 if (!NT_STATUS_IS_OK(get_file_handle_for_metadata(conn,
465 smb_fname,
466 &fsp,
467 &need_close))) {
468 return false;
471 become_root();
472 if (SMB_VFS_FSETXATTR(fsp,
473 SAMBA_XATTR_DOS_ATTRIB, blob.data,
474 blob.length, 0) == 0) {
475 ret = true;
477 unbecome_root();
478 if (need_close) {
479 close_file(NULL, fsp, NORMAL_CLOSE);
481 return ret;
483 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
484 (unsigned int)dosmode,
485 smb_fname_str_dbg(smb_fname)));
486 return true;
489 /****************************************************************************
490 Change a unix mode to a dos mode for an ms dfs link.
491 ****************************************************************************/
493 uint32 dos_mode_msdfs(connection_struct *conn,
494 const struct smb_filename *smb_fname)
496 uint32 result = 0;
498 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
500 if (!VALID_STAT(smb_fname->st)) {
501 return 0;
504 /* First do any modifications that depend on the path name. */
505 /* hide files with a name starting with a . */
506 if (lp_hide_dot_files(SNUM(conn))) {
507 const char *p = strrchr_m(smb_fname->base_name, '/');
508 if (p) {
509 p++;
510 } else {
511 p = smb_fname->base_name;
514 /* Only . and .. are not hidden. */
515 if (p[0] == '.' && !((p[1] == '\0') ||
516 (p[1] == '.' && p[2] == '\0'))) {
517 result |= FILE_ATTRIBUTE_HIDDEN;
521 result |= dos_mode_from_sbuf(conn, smb_fname);
523 /* Optimization : Only call is_hidden_path if it's not already
524 hidden. */
525 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
526 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
527 result |= FILE_ATTRIBUTE_HIDDEN;
530 if (result == 0) {
531 result = FILE_ATTRIBUTE_NORMAL;
534 result = filter_mode_by_protocol(result);
537 * Add in that it is a reparse point
539 result |= FILE_ATTRIBUTE_REPARSE_POINT;
541 DEBUG(8,("dos_mode_msdfs returning "));
543 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
544 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
545 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
546 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
547 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
548 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
550 DEBUG(8,("\n"));
552 return(result);
556 * check whether a file or directory is flagged as compressed.
558 static NTSTATUS dos_mode_check_compressed(connection_struct *conn,
559 struct smb_filename *smb_fname,
560 bool *is_compressed)
562 NTSTATUS status;
563 uint16_t compression_fmt;
564 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
565 if (tmp_ctx == NULL) {
566 status = NT_STATUS_NO_MEMORY;
567 goto err_out;
570 status = SMB_VFS_GET_COMPRESSION(conn, tmp_ctx, NULL, smb_fname,
571 &compression_fmt);
572 if (!NT_STATUS_IS_OK(status)) {
573 goto err_ctx_free;
576 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
577 *is_compressed = true;
578 } else {
579 *is_compressed = false;
581 status = NT_STATUS_OK;
583 err_ctx_free:
584 talloc_free(tmp_ctx);
585 err_out:
586 return status;
589 /****************************************************************************
590 Change a unix mode to a dos mode.
591 May also read the create timespec into the stat struct in smb_fname
592 if "store dos attributes" is true.
593 ****************************************************************************/
595 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
597 uint32 result = 0;
598 bool offline;
600 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
602 if (!VALID_STAT(smb_fname->st)) {
603 return 0;
606 /* First do any modifications that depend on the path name. */
607 /* hide files with a name starting with a . */
608 if (lp_hide_dot_files(SNUM(conn))) {
609 const char *p = strrchr_m(smb_fname->base_name,'/');
610 if (p) {
611 p++;
612 } else {
613 p = smb_fname->base_name;
616 /* Only . and .. are not hidden. */
617 if (p[0] == '.' && !((p[1] == '\0') ||
618 (p[1] == '.' && p[2] == '\0'))) {
619 result |= FILE_ATTRIBUTE_HIDDEN;
623 /* Get the DOS attributes from an EA by preference. */
624 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
625 result |= dos_mode_from_sbuf(conn, smb_fname);
628 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
629 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
630 result |= FILE_ATTRIBUTE_OFFLINE;
633 if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
634 bool compressed = false;
635 NTSTATUS status = dos_mode_check_compressed(conn, smb_fname,
636 &compressed);
637 if (NT_STATUS_IS_OK(status) && compressed) {
638 result |= FILE_ATTRIBUTE_COMPRESSED;
642 /* Optimization : Only call is_hidden_path if it's not already
643 hidden. */
644 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
645 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
646 result |= FILE_ATTRIBUTE_HIDDEN;
649 if (result == 0) {
650 result = FILE_ATTRIBUTE_NORMAL;
653 result = filter_mode_by_protocol(result);
655 dos_mode_debug_print(result);
657 return result;
660 /*******************************************************************
661 chmod a file - but preserve some bits.
662 If "store dos attributes" is also set it will store the create time
663 from the stat struct in smb_fname (in NTTIME format) in the EA
664 attribute also.
665 ********************************************************************/
667 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
668 uint32 dosmode, const char *parent_dir, bool newfile)
670 int mask=0;
671 mode_t tmp;
672 mode_t unixmode;
673 int ret = -1, lret = -1;
674 uint32_t old_mode;
675 struct timespec new_create_timespec;
676 files_struct *fsp = NULL;
677 bool need_close = false;
678 NTSTATUS status;
680 if (!CAN_WRITE(conn)) {
681 errno = EROFS;
682 return -1;
685 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
686 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
688 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
689 dosmode, smb_fname_str_dbg(smb_fname)));
691 unixmode = smb_fname->st.st_ex_mode;
693 get_acl_group_bits(conn, smb_fname->base_name,
694 &smb_fname->st.st_ex_mode);
696 if (S_ISDIR(smb_fname->st.st_ex_mode))
697 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
698 else
699 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
701 new_create_timespec = smb_fname->st.st_ex_btime;
703 old_mode = dos_mode(conn, smb_fname);
705 if ((dosmode & FILE_ATTRIBUTE_OFFLINE) &&
706 !(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
707 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
708 if (lret == -1) {
709 if (errno == ENOTSUP) {
710 DEBUG(10, ("Setting FILE_ATTRIBUTE_OFFLINE for "
711 "%s/%s is not supported.\n",
712 parent_dir,
713 smb_fname_str_dbg(smb_fname)));
714 } else {
715 DEBUG(0, ("An error occurred while setting "
716 "FILE_ATTRIBUTE_OFFLINE for "
717 "%s/%s: %s", parent_dir,
718 smb_fname_str_dbg(smb_fname),
719 strerror(errno)));
724 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
725 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
727 smb_fname->st.st_ex_btime = new_create_timespec;
729 /* Store the DOS attributes in an EA by preference. */
730 if (lp_store_dos_attributes(SNUM(conn))) {
732 * Don't fall back to using UNIX modes. Finally
733 * follow the smb.conf manpage.
735 if (!set_ea_dos_attribute(conn, smb_fname, dosmode)) {
736 return -1;
738 if (!newfile) {
739 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
740 FILE_NOTIFY_CHANGE_ATTRIBUTES,
741 smb_fname->base_name);
743 smb_fname->st.st_ex_mode = unixmode;
744 return 0;
747 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
749 /* preserve the file type bits */
750 mask |= S_IFMT;
752 /* preserve the s bits */
753 mask |= (S_ISUID | S_ISGID);
755 /* preserve the t bit */
756 #ifdef S_ISVTX
757 mask |= S_ISVTX;
758 #endif
760 /* possibly preserve the x bits */
761 if (!MAP_ARCHIVE(conn))
762 mask |= S_IXUSR;
763 if (!MAP_SYSTEM(conn))
764 mask |= S_IXGRP;
765 if (!MAP_HIDDEN(conn))
766 mask |= S_IXOTH;
768 unixmode |= (smb_fname->st.st_ex_mode & mask);
770 /* if we previously had any r bits set then leave them alone */
771 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
772 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
773 unixmode |= tmp;
776 /* if we previously had any w bits set then leave them alone
777 whilst adding in the new w bits, if the new mode is not rdonly */
778 if (!IS_DOS_READONLY(dosmode)) {
779 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
783 * From the chmod 2 man page:
785 * "If the calling process is not privileged, and the group of the file
786 * does not match the effective group ID of the process or one of its
787 * supplementary group IDs, the S_ISGID bit will be turned off, but
788 * this will not cause an error to be returned."
790 * Simply refuse to do the chmod in this case.
793 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
794 geteuid() != sec_initial_uid() &&
795 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
796 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
797 "set for directory %s\n",
798 smb_fname_str_dbg(smb_fname)));
799 errno = EPERM;
800 return -1;
803 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
804 if (ret == 0) {
805 if(!newfile || (lret != -1)) {
806 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
807 FILE_NOTIFY_CHANGE_ATTRIBUTES,
808 smb_fname->base_name);
810 smb_fname->st.st_ex_mode = unixmode;
811 return 0;
814 if((errno != EPERM) && (errno != EACCES))
815 return -1;
817 if(!lp_dos_filemode(SNUM(conn)))
818 return -1;
820 /* We want DOS semantics, ie allow non owner with write permission to change the
821 bits on a file. Just like file_ntimes below.
824 if (!can_write_to_file(conn, smb_fname)) {
825 errno = EACCES;
826 return -1;
830 * We need to get an open file handle to do the
831 * metadata operation under root.
834 status = get_file_handle_for_metadata(conn,
835 smb_fname,
836 &fsp,
837 &need_close);
838 if (!NT_STATUS_IS_OK(status)) {
839 errno = map_errno_from_nt_status(status);
840 return -1;
843 become_root();
844 ret = SMB_VFS_FCHMOD(fsp, unixmode);
845 unbecome_root();
846 if (need_close) {
847 close_file(NULL, fsp, NORMAL_CLOSE);
849 if (!newfile) {
850 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
851 FILE_NOTIFY_CHANGE_ATTRIBUTES,
852 smb_fname->base_name);
854 if (ret == 0) {
855 smb_fname->st.st_ex_mode = unixmode;
858 return( ret );
862 NTSTATUS file_set_sparse(connection_struct *conn,
863 files_struct *fsp,
864 bool sparse)
866 uint32_t old_dosmode;
867 uint32_t new_dosmode;
868 NTSTATUS status;
870 if (!CAN_WRITE(conn)) {
871 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
872 "on readonly share[%s]\n",
873 smb_fname_str_dbg(fsp->fsp_name),
874 sparse,
875 lp_servicename(talloc_tos(), SNUM(conn))));
876 return NT_STATUS_MEDIA_WRITE_PROTECTED;
880 * Windows Server 2008 & 2012 permit FSCTL_SET_SPARSE if any of the
881 * following access flags are granted.
883 if ((fsp->access_mask & (FILE_WRITE_DATA
884 | FILE_WRITE_ATTRIBUTES
885 | SEC_FILE_APPEND_DATA)) == 0) {
886 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
887 "access_mask[0x%08X] - access denied\n",
888 smb_fname_str_dbg(fsp->fsp_name),
889 sparse,
890 fsp->access_mask));
891 return NT_STATUS_ACCESS_DENIED;
894 if (fsp->is_directory) {
895 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
896 (sparse ? "set" : "clear"),
897 smb_fname_str_dbg(fsp->fsp_name)));
898 return NT_STATUS_INVALID_PARAMETER;
901 if (IS_IPC(conn) || IS_PRINT(conn)) {
902 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
903 (sparse ? "set" : "clear")));
904 return NT_STATUS_INVALID_PARAMETER;
907 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
908 sparse, smb_fname_str_dbg(fsp->fsp_name)));
910 if (!lp_store_dos_attributes(SNUM(conn))) {
911 return NT_STATUS_INVALID_DEVICE_REQUEST;
914 status = vfs_stat_fsp(fsp);
915 if (!NT_STATUS_IS_OK(status)) {
916 return status;
919 old_dosmode = dos_mode(conn, fsp->fsp_name);
921 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
922 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
923 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
924 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
925 } else {
926 return NT_STATUS_OK;
929 /* Store the DOS attributes in an EA. */
930 if (!set_ea_dos_attribute(conn, fsp->fsp_name,
931 new_dosmode)) {
932 if (errno == 0) {
933 errno = EIO;
935 return map_nt_error_from_unix(errno);
938 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
939 FILE_NOTIFY_CHANGE_ATTRIBUTES,
940 fsp->fsp_name->base_name);
942 fsp->is_sparse = sparse;
944 return NT_STATUS_OK;
947 /*******************************************************************
948 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
949 than POSIX.
950 *******************************************************************/
952 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
953 struct smb_file_time *ft)
955 int ret = -1;
957 errno = 0;
959 DEBUG(6, ("file_ntime: actime: %s",
960 time_to_asc(convert_timespec_to_time_t(ft->atime))));
961 DEBUG(6, ("file_ntime: modtime: %s",
962 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
963 DEBUG(6, ("file_ntime: ctime: %s",
964 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
965 DEBUG(6, ("file_ntime: createtime: %s",
966 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
968 /* Don't update the time on read-only shares */
969 /* We need this as set_filetime (which can be called on
970 close and other paths) can end up calling this function
971 without the NEED_WRITE protection. Found by :
972 Leo Weppelman <leo@wau.mis.ah.nl>
975 if (!CAN_WRITE(conn)) {
976 return 0;
979 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
980 return 0;
983 if((errno != EPERM) && (errno != EACCES)) {
984 return -1;
987 if(!lp_dos_filetimes(SNUM(conn))) {
988 return -1;
991 /* We have permission (given by the Samba admin) to
992 break POSIX semantics and allow a user to change
993 the time on a file they don't own but can write to
994 (as DOS does).
997 /* Check if we have write access. */
998 if (can_write_to_file(conn, smb_fname)) {
999 /* We are allowed to become root and change the filetime. */
1000 become_root();
1001 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1002 unbecome_root();
1005 return ret;
1008 /******************************************************************
1009 Force a "sticky" write time on a pathname. This will always be
1010 returned on all future write time queries and set on close.
1011 ******************************************************************/
1013 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1015 if (null_timespec(mtime)) {
1016 return true;
1019 if (!set_sticky_write_time(fileid, mtime)) {
1020 return false;
1023 return true;
1026 /******************************************************************
1027 Force a "sticky" write time on an fsp. This will always be
1028 returned on all future write time queries and set on close.
1029 ******************************************************************/
1031 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1033 if (null_timespec(mtime)) {
1034 return true;
1037 fsp->write_time_forced = true;
1038 TALLOC_FREE(fsp->update_write_time_event);
1040 return set_sticky_write_time_path(fsp->file_id, mtime);
1043 /******************************************************************
1044 Set a create time EA.
1045 ******************************************************************/
1047 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1048 const struct smb_filename *psmb_fname,
1049 struct timespec create_time)
1051 struct smb_filename *smb_fname;
1052 uint32_t dosmode;
1053 int ret;
1055 if (!lp_store_dos_attributes(SNUM(conn))) {
1056 return NT_STATUS_OK;
1059 smb_fname = synthetic_smb_fname(talloc_tos(), psmb_fname->base_name,
1060 NULL, &psmb_fname->st);
1062 if (smb_fname == NULL) {
1063 return NT_STATUS_NO_MEMORY;
1066 dosmode = dos_mode(conn, smb_fname);
1068 smb_fname->st.st_ex_btime = create_time;
1070 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1071 if (ret == -1) {
1072 map_nt_error_from_unix(errno);
1075 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1076 smb_fname_str_dbg(smb_fname)));
1078 return NT_STATUS_OK;
1081 /******************************************************************
1082 Return a create time.
1083 ******************************************************************/
1085 struct timespec get_create_timespec(connection_struct *conn,
1086 struct files_struct *fsp,
1087 const struct smb_filename *smb_fname)
1089 return smb_fname->st.st_ex_btime;
1092 /******************************************************************
1093 Return a change time (may look at EA in future).
1094 ******************************************************************/
1096 struct timespec get_change_timespec(connection_struct *conn,
1097 struct files_struct *fsp,
1098 const struct smb_filename *smb_fname)
1100 return smb_fname->st.st_ex_mtime;
1103 /****************************************************************************
1104 Get a real open file handle we can do meta-data operations on. As it's
1105 going to be used under root access only on meta-data we should look for
1106 any existing open file handle first, and use that in preference (also to
1107 avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1108 ****************************************************************************/
1110 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1111 struct smb_filename *smb_fname,
1112 files_struct **ret_fsp,
1113 bool *need_close)
1115 NTSTATUS status;
1116 files_struct *fsp;
1117 struct file_id file_id;
1119 *need_close = false;
1121 if (!VALID_STAT(smb_fname->st)) {
1122 return NT_STATUS_INVALID_PARAMETER;
1125 file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1127 for(fsp = file_find_di_first(conn->sconn, file_id);
1128 fsp;
1129 fsp = file_find_di_next(fsp)) {
1130 if (fsp->fh->fd != -1) {
1131 *ret_fsp = fsp;
1132 return NT_STATUS_OK;
1136 /* Opens an INTERNAL_OPEN_ONLY write handle. */
1137 status = SMB_VFS_CREATE_FILE(
1138 conn, /* conn */
1139 NULL, /* req */
1140 0, /* root_dir_fid */
1141 smb_fname, /* fname */
1142 FILE_WRITE_DATA, /* access_mask */
1143 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
1144 FILE_SHARE_DELETE),
1145 FILE_OPEN, /* create_disposition*/
1146 0, /* create_options */
1147 0, /* file_attributes */
1148 INTERNAL_OPEN_ONLY, /* oplock_request */
1149 NULL, /* lease */
1150 0, /* allocation_size */
1151 0, /* private_flags */
1152 NULL, /* sd */
1153 NULL, /* ea_list */
1154 ret_fsp, /* result */
1155 NULL, /* pinfo */
1156 NULL, NULL); /* create context */
1158 if (NT_STATUS_IS_OK(status)) {
1159 *need_close = true;
1161 return status;