Revert "wafbuild: use -Wstack-protector if available"
[Samba/vl.git] / source3 / smbd / dosmode.c
blob1949006c1939b0b1b9a5679e019bf028d13699a8
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 "../libcli/security/security.h"
25 #include "smbd/smbd.h"
26 #include "lib/param/loadparm.h"
28 static uint32_t filter_mode_by_protocol(uint32_t mode)
30 if (get_Protocol() <= PROTOCOL_LANMAN2) {
31 DEBUG(10,("filter_mode_by_protocol: "
32 "filtering result 0x%x to 0x%x\n",
33 (unsigned int)mode,
34 (unsigned int)(mode & 0x3f) ));
35 mode &= 0x3f;
37 return mode;
40 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
42 #ifdef S_ISLNK
43 #if LINKS_READ_ONLY
44 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
45 return FILE_ATTRIBUTE_READONLY;
46 #endif
47 #endif
48 return 0;
51 /****************************************************************************
52 Change a dos mode to a unix mode.
53 Base permission for files:
54 if creating file and inheriting (i.e. parent_dir != NULL)
55 apply read/write bits from parent directory.
56 else
57 everybody gets read bit set
58 dos readonly is represented in unix by removing everyone's write bit
59 dos archive is represented in unix by the user's execute bit
60 dos system is represented in unix by the group's execute bit
61 dos hidden is represented in unix by the other's execute bit
62 if !inheriting {
63 Then apply create mask,
64 then add force bits.
66 Base permission for directories:
67 dos directory is represented in unix by unix's dir bit and the exec bit
68 if !inheriting {
69 Then apply create mask,
70 then add force bits.
72 ****************************************************************************/
74 mode_t unix_mode(connection_struct *conn, int dosmode,
75 const struct smb_filename *smb_fname,
76 const char *inherit_from_dir)
78 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
79 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
80 * inheriting. */
82 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
83 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
86 if ((inherit_from_dir != NULL) && lp_inherit_perms(SNUM(conn))) {
87 struct smb_filename *smb_fname_parent = NULL;
88 NTSTATUS status;
90 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
91 smb_fname_str_dbg(smb_fname),
92 inherit_from_dir));
94 status = create_synthetic_smb_fname(talloc_tos(),
95 inherit_from_dir, NULL,
96 NULL, &smb_fname_parent);
97 if (!NT_STATUS_IS_OK(status)) {
98 DEBUG(1,("unix_mode(%s) failed, [dir %s]: %s\n",
99 smb_fname_str_dbg(smb_fname),
100 inherit_from_dir, nt_errstr(status)));
101 return(0);
104 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
105 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
106 smb_fname_str_dbg(smb_fname),
107 inherit_from_dir, strerror(errno)));
108 TALLOC_FREE(smb_fname_parent);
109 return(0); /* *** shouldn't happen! *** */
112 /* Save for later - but explicitly remove setuid bit for safety. */
113 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
114 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
115 smb_fname_str_dbg(smb_fname), (int)dir_mode));
116 /* Clear "result" */
117 result = 0;
118 TALLOC_FREE(smb_fname_parent);
121 if (IS_DOS_DIR(dosmode)) {
122 /* We never make directories read only for the owner as under DOS a user
123 can always create a file in a read-only directory. */
124 result |= (S_IFDIR | S_IWUSR);
126 if (dir_mode) {
127 /* Inherit mode of parent directory. */
128 result |= dir_mode;
129 } else {
130 /* Provisionally add all 'x' bits */
131 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
133 /* Apply directory mask */
134 result &= lp_dir_mask(SNUM(conn));
135 /* Add in force bits */
136 result |= lp_force_dir_mode(SNUM(conn));
138 } else {
139 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
140 result |= S_IXUSR;
142 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
143 result |= S_IXGRP;
145 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
146 result |= S_IXOTH;
148 if (dir_mode) {
149 /* Inherit 666 component of parent directory mode */
150 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
151 } else {
152 /* Apply mode mask */
153 result &= lp_create_mask(SNUM(conn));
154 /* Add in force bits */
155 result |= lp_force_create_mode(SNUM(conn));
159 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
160 (int)result));
161 return(result);
164 /****************************************************************************
165 Change a unix mode to a dos mode.
166 ****************************************************************************/
168 static uint32 dos_mode_from_sbuf(connection_struct *conn,
169 const struct smb_filename *smb_fname)
171 int result = 0;
172 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
174 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
175 /* if we can find out if a file is immutable we should report it r/o */
176 if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
177 result |= FILE_ATTRIBUTE_READONLY;
179 #endif
180 if (ro_opts == MAP_READONLY_YES) {
181 /* Original Samba method - map inverse of user "w" bit. */
182 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
183 result |= FILE_ATTRIBUTE_READONLY;
185 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
186 /* Check actual permissions for read-only. */
187 if (!can_write_to_file(conn, smb_fname)) {
188 result |= FILE_ATTRIBUTE_READONLY;
190 } /* Else never set the readonly bit. */
192 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
193 result |= FILE_ATTRIBUTE_ARCHIVE;
195 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
196 result |= FILE_ATTRIBUTE_SYSTEM;
198 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
199 result |= FILE_ATTRIBUTE_HIDDEN;
201 if (S_ISDIR(smb_fname->st.st_ex_mode))
202 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
204 result |= set_link_read_only_flag(&smb_fname->st);
206 DEBUG(8,("dos_mode_from_sbuf returning "));
208 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
209 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
210 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
211 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
212 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
214 DEBUG(8,("\n"));
215 return result;
218 /****************************************************************************
219 Get DOS attributes from an EA.
220 This can also pull the create time into the stat struct inside smb_fname.
221 ****************************************************************************/
223 static bool get_ea_dos_attribute(connection_struct *conn,
224 struct smb_filename *smb_fname,
225 uint32 *pattr)
227 struct xattr_DOSATTRIB dosattrib;
228 enum ndr_err_code ndr_err;
229 DATA_BLOB blob;
230 ssize_t sizeret;
231 fstring attrstr;
232 uint32_t dosattr;
234 if (!lp_store_dos_attributes(SNUM(conn))) {
235 return False;
238 /* Don't reset pattr to zero as we may already have filename-based attributes we
239 need to preserve. */
241 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
242 SAMBA_XATTR_DOS_ATTRIB, attrstr,
243 sizeof(attrstr));
244 if (sizeret == -1) {
245 if (errno == ENOSYS
246 #if defined(ENOTSUP)
247 || errno == ENOTSUP) {
248 #else
250 #endif
251 DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
252 "from EA on file %s: Error = %s\n",
253 smb_fname_str_dbg(smb_fname),
254 strerror(errno)));
255 set_store_dos_attributes(SNUM(conn), False);
257 return False;
260 blob.data = (uint8_t *)attrstr;
261 blob.length = sizeret;
263 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
264 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
266 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
267 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
268 "from EA on file %s: Error = %s\n",
269 smb_fname_str_dbg(smb_fname),
270 ndr_errstr(ndr_err)));
271 return false;
274 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
275 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
277 switch (dosattrib.version) {
278 case 0xFFFF:
279 dosattr = dosattrib.info.compatinfoFFFF.attrib;
280 break;
281 case 1:
282 dosattr = dosattrib.info.info1.attrib;
283 if (!null_nttime(dosattrib.info.info1.create_time)) {
284 struct timespec create_time =
285 nt_time_to_unix_timespec(
286 &dosattrib.info.info1.create_time);
288 update_stat_ex_create_time(&smb_fname->st,
289 create_time);
291 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
292 "set btime %s\n",
293 smb_fname_str_dbg(smb_fname),
294 time_to_asc(convert_timespec_to_time_t(
295 create_time)) ));
297 break;
298 case 2:
299 dosattr = dosattrib.info.oldinfo2.attrib;
300 /* Don't know what flags to check for this case. */
301 break;
302 case 3:
303 dosattr = dosattrib.info.info3.attrib;
304 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
305 !null_nttime(dosattrib.info.info3.create_time)) {
306 struct timespec create_time =
307 nt_time_to_unix_timespec(
308 &dosattrib.info.info3.create_time);
310 update_stat_ex_create_time(&smb_fname->st,
311 create_time);
313 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
314 "set btime %s\n",
315 smb_fname_str_dbg(smb_fname),
316 time_to_asc(convert_timespec_to_time_t(
317 create_time)) ));
319 break;
320 default:
321 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
322 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
323 attrstr));
324 return false;
327 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
328 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
330 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
331 *pattr = (uint32)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
333 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
335 if (dosattr & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
336 if (dosattr & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
337 if (dosattr & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
338 if (dosattr & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
339 if (dosattr & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
341 DEBUG(8,("\n"));
343 return True;
346 /****************************************************************************
347 Set DOS attributes in an EA.
348 Also sets the create time.
349 ****************************************************************************/
351 static bool set_ea_dos_attribute(connection_struct *conn,
352 struct smb_filename *smb_fname,
353 uint32 dosmode)
355 struct xattr_DOSATTRIB dosattrib;
356 enum ndr_err_code ndr_err;
357 DATA_BLOB blob;
359 if (!lp_store_dos_attributes(SNUM(conn))) {
360 return False;
363 ZERO_STRUCT(dosattrib);
364 ZERO_STRUCT(blob);
366 dosattrib.version = 3;
367 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
368 XATTR_DOSINFO_CREATE_TIME;
369 dosattrib.info.info3.attrib = dosmode;
370 unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
371 smb_fname->st.st_ex_btime);
373 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
374 (unsigned int)dosmode,
375 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
376 smb_fname_str_dbg(smb_fname) ));
378 ndr_err = ndr_push_struct_blob(
379 &blob, talloc_tos(), &dosattrib,
380 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
382 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
383 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
384 ndr_errstr(ndr_err)));
385 return false;
388 if (blob.data == NULL || blob.length == 0) {
389 return false;
392 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
393 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
394 0) == -1) {
395 bool ret = false;
396 files_struct *fsp = NULL;
398 if((errno != EPERM) && (errno != EACCES)) {
399 if (errno == ENOSYS
400 #if defined(ENOTSUP)
401 || errno == ENOTSUP) {
402 #else
404 #endif
405 DEBUG(1,("set_ea_dos_attributes: Cannot set "
406 "attribute EA on file %s: Error = %s\n",
407 smb_fname_str_dbg(smb_fname),
408 strerror(errno) ));
409 set_store_dos_attributes(SNUM(conn), False);
411 return false;
414 /* We want DOS semantics, ie allow non owner with write permission to change the
415 bits on a file. Just like file_ntimes below.
418 /* Check if we have write access. */
419 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
420 return false;
423 * We need to open the file with write access whilst
424 * still in our current user context. This ensures we
425 * are not violating security in doing the setxattr.
428 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
429 &fsp)))
430 return false;
431 become_root();
432 if (SMB_VFS_FSETXATTR(fsp,
433 SAMBA_XATTR_DOS_ATTRIB, blob.data,
434 blob.length, 0) == 0) {
435 ret = true;
437 unbecome_root();
438 close_file(NULL, fsp, NORMAL_CLOSE);
439 return ret;
441 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
442 (unsigned int)dosmode,
443 smb_fname_str_dbg(smb_fname)));
444 return true;
447 /****************************************************************************
448 Change a unix mode to a dos mode for an ms dfs link.
449 ****************************************************************************/
451 uint32 dos_mode_msdfs(connection_struct *conn,
452 const struct smb_filename *smb_fname)
454 uint32 result = 0;
456 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
458 if (!VALID_STAT(smb_fname->st)) {
459 return 0;
462 /* First do any modifications that depend on the path name. */
463 /* hide files with a name starting with a . */
464 if (lp_hide_dot_files(SNUM(conn))) {
465 const char *p = strrchr_m(smb_fname->base_name, '/');
466 if (p) {
467 p++;
468 } else {
469 p = smb_fname->base_name;
472 /* Only . and .. are not hidden. */
473 if (p[0] == '.' && !((p[1] == '\0') ||
474 (p[1] == '.' && p[2] == '\0'))) {
475 result |= FILE_ATTRIBUTE_HIDDEN;
479 result |= dos_mode_from_sbuf(conn, smb_fname);
481 /* Optimization : Only call is_hidden_path if it's not already
482 hidden. */
483 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
484 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
485 result |= FILE_ATTRIBUTE_HIDDEN;
488 if (result == 0) {
489 result = FILE_ATTRIBUTE_NORMAL;
492 result = filter_mode_by_protocol(result);
494 DEBUG(8,("dos_mode_msdfs returning "));
496 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
497 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
498 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
499 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
500 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
501 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
503 DEBUG(8,("\n"));
505 return(result);
508 #ifdef HAVE_STAT_DOS_FLAGS
509 /****************************************************************************
510 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
511 ****************************************************************************/
513 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
515 uint32_t dos_stat_flags = 0;
517 if (dosmode & FILE_ATTRIBUTE_ARCHIVE)
518 dos_stat_flags |= UF_DOS_ARCHIVE;
519 if (dosmode & FILE_ATTRIBUTE_HIDDEN)
520 dos_stat_flags |= UF_DOS_HIDDEN;
521 if (dosmode & FILE_ATTRIBUTE_READONLY)
522 dos_stat_flags |= UF_DOS_RO;
523 if (dosmode & FILE_ATTRIBUTE_SYSTEM)
524 dos_stat_flags |= UF_DOS_SYSTEM;
525 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
526 dos_stat_flags |= UF_DOS_NOINDEX;
528 return dos_stat_flags;
531 /****************************************************************************
532 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
533 ****************************************************************************/
535 static bool get_stat_dos_flags(connection_struct *conn,
536 const struct smb_filename *smb_fname,
537 uint32_t *dosmode)
539 SMB_ASSERT(VALID_STAT(smb_fname->st));
540 SMB_ASSERT(dosmode);
542 if (!lp_store_dos_attributes(SNUM(conn))) {
543 return false;
546 DEBUG(5, ("Getting stat dos attributes for %s.\n",
547 smb_fname_str_dbg(smb_fname)));
549 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
550 *dosmode |= FILE_ATTRIBUTE_ARCHIVE;
551 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
552 *dosmode |= FILE_ATTRIBUTE_HIDDEN;
553 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
554 *dosmode |= FILE_ATTRIBUTE_READONLY;
555 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
556 *dosmode |= FILE_ATTRIBUTE_SYSTEM;
557 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
558 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
559 if (smb_fname->st.st_ex_flags & FILE_ATTRIBUTE_SPARSE)
560 *dosmode |= FILE_ATTRIBUTE_SPARSE;
561 if (S_ISDIR(smb_fname->st.st_ex_mode))
562 *dosmode |= FILE_ATTRIBUTE_DIRECTORY;
564 *dosmode |= set_link_read_only_flag(&smb_fname->st);
566 return true;
569 /****************************************************************************
570 Sets DOS attributes, stored in st_ex_flags of the inode.
571 ****************************************************************************/
573 static bool set_stat_dos_flags(connection_struct *conn,
574 const struct smb_filename *smb_fname,
575 uint32_t dosmode,
576 bool *attributes_changed)
578 uint32_t new_flags = 0;
579 int error = 0;
581 SMB_ASSERT(VALID_STAT(smb_fname->st));
582 SMB_ASSERT(attributes_changed);
584 *attributes_changed = false;
586 if (!lp_store_dos_attributes(SNUM(conn))) {
587 return false;
590 DEBUG(5, ("Setting stat dos attributes for %s.\n",
591 smb_fname_str_dbg(smb_fname)));
593 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
594 dos_attributes_to_stat_dos_flags(dosmode);
596 /* Return early if no flags changed. */
597 if (new_flags == smb_fname->st.st_ex_flags)
598 return true;
600 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
601 smb_fname->st.st_ex_flags));
603 /* Set new flags with chflags. */
604 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
605 if (error) {
606 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
607 "file %s! errno=%d\n", new_flags,
608 smb_fname_str_dbg(smb_fname), errno));
609 return false;
612 *attributes_changed = true;
613 return true;
615 #endif /* HAVE_STAT_DOS_FLAGS */
617 /****************************************************************************
618 Change a unix mode to a dos mode.
619 May also read the create timespec into the stat struct in smb_fname
620 if "store dos attributes" is true.
621 ****************************************************************************/
623 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
625 uint32 result = 0;
626 bool offline, used_stat_dos_flags = false;
628 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
630 if (!VALID_STAT(smb_fname->st)) {
631 return 0;
634 /* First do any modifications that depend on the path name. */
635 /* hide files with a name starting with a . */
636 if (lp_hide_dot_files(SNUM(conn))) {
637 const char *p = strrchr_m(smb_fname->base_name,'/');
638 if (p) {
639 p++;
640 } else {
641 p = smb_fname->base_name;
644 /* Only . and .. are not hidden. */
645 if (p[0] == '.' && !((p[1] == '\0') ||
646 (p[1] == '.' && p[2] == '\0'))) {
647 result |= FILE_ATTRIBUTE_HIDDEN;
651 #ifdef HAVE_STAT_DOS_FLAGS
652 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
653 #endif
654 if (!used_stat_dos_flags) {
655 /* Get the DOS attributes from an EA by preference. */
656 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
657 result |= dos_mode_from_sbuf(conn, smb_fname);
661 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
662 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
663 result |= FILE_ATTRIBUTE_OFFLINE;
666 /* Optimization : Only call is_hidden_path if it's not already
667 hidden. */
668 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
669 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
670 result |= FILE_ATTRIBUTE_HIDDEN;
673 if (result == 0) {
674 result = FILE_ATTRIBUTE_NORMAL;
677 result = filter_mode_by_protocol(result);
679 DEBUG(8,("dos_mode returning "));
681 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
682 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
683 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
684 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
685 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
686 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
687 if (result & FILE_ATTRIBUTE_OFFLINE ) DEBUG(8, ("[offline]"));
689 DEBUG(8,("\n"));
691 return(result);
694 /*******************************************************************
695 chmod a file - but preserve some bits.
696 If "store dos attributes" is also set it will store the create time
697 from the stat struct in smb_fname (in NTTIME format) in the EA
698 attribute also.
699 ********************************************************************/
701 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
702 uint32 dosmode, const char *parent_dir, bool newfile)
704 int mask=0;
705 mode_t tmp;
706 mode_t unixmode;
707 int ret = -1, lret = -1;
708 uint32_t old_mode;
709 struct timespec new_create_timespec;
711 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
712 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
714 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
715 dosmode, smb_fname_str_dbg(smb_fname)));
717 unixmode = smb_fname->st.st_ex_mode;
719 get_acl_group_bits(conn, smb_fname->base_name,
720 &smb_fname->st.st_ex_mode);
722 if (S_ISDIR(smb_fname->st.st_ex_mode))
723 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
724 else
725 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
727 new_create_timespec = smb_fname->st.st_ex_btime;
729 old_mode = dos_mode(conn, smb_fname);
731 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
732 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
733 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
734 if (lret == -1) {
735 DEBUG(0, ("set_dos_mode: client has asked to "
736 "set FILE_ATTRIBUTE_OFFLINE to "
737 "%s/%s but there was an error while "
738 "setting it or it is not "
739 "supported.\n", parent_dir,
740 smb_fname_str_dbg(smb_fname)));
745 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
746 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
748 smb_fname->st.st_ex_btime = new_create_timespec;
750 #ifdef HAVE_STAT_DOS_FLAGS
752 bool attributes_changed;
754 if (set_stat_dos_flags(conn, smb_fname, dosmode,
755 &attributes_changed))
757 if (!newfile && attributes_changed) {
758 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
759 FILE_NOTIFY_CHANGE_ATTRIBUTES,
760 smb_fname->base_name);
762 smb_fname->st.st_ex_mode = unixmode;
763 return 0;
766 #endif
767 /* Store the DOS attributes in an EA by preference. */
768 if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
769 if (!newfile) {
770 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
771 FILE_NOTIFY_CHANGE_ATTRIBUTES,
772 smb_fname->base_name);
774 smb_fname->st.st_ex_mode = unixmode;
775 return 0;
778 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
780 /* preserve the file type bits */
781 mask |= S_IFMT;
783 /* preserve the s bits */
784 mask |= (S_ISUID | S_ISGID);
786 /* preserve the t bit */
787 #ifdef S_ISVTX
788 mask |= S_ISVTX;
789 #endif
791 /* possibly preserve the x bits */
792 if (!MAP_ARCHIVE(conn))
793 mask |= S_IXUSR;
794 if (!MAP_SYSTEM(conn))
795 mask |= S_IXGRP;
796 if (!MAP_HIDDEN(conn))
797 mask |= S_IXOTH;
799 unixmode |= (smb_fname->st.st_ex_mode & mask);
801 /* if we previously had any r bits set then leave them alone */
802 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
803 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
804 unixmode |= tmp;
807 /* if we previously had any w bits set then leave them alone
808 whilst adding in the new w bits, if the new mode is not rdonly */
809 if (!IS_DOS_READONLY(dosmode)) {
810 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
814 * From the chmod 2 man page:
816 * "If the calling process is not privileged, and the group of the file
817 * does not match the effective group ID of the process or one of its
818 * supplementary group IDs, the S_ISGID bit will be turned off, but
819 * this will not cause an error to be returned."
821 * Simply refuse to do the chmod in this case.
824 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
825 geteuid() != sec_initial_uid() &&
826 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
827 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
828 "set for directory %s\n",
829 smb_fname_str_dbg(smb_fname)));
830 errno = EPERM;
831 return -1;
834 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
835 if (ret == 0) {
836 if(!newfile || (lret != -1)) {
837 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
838 FILE_NOTIFY_CHANGE_ATTRIBUTES,
839 smb_fname->base_name);
841 smb_fname->st.st_ex_mode = unixmode;
842 return 0;
845 if((errno != EPERM) && (errno != EACCES))
846 return -1;
848 if(!lp_dos_filemode(SNUM(conn)))
849 return -1;
851 /* We want DOS semantics, ie allow non owner with write permission to change the
852 bits on a file. Just like file_ntimes below.
855 /* Check if we have write access. */
856 if (CAN_WRITE(conn)) {
858 * We need to open the file with write access whilst
859 * still in our current user context. This ensures we
860 * are not violating security in doing the fchmod.
862 files_struct *fsp;
863 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
864 &fsp)))
865 return -1;
866 become_root();
867 ret = SMB_VFS_FCHMOD(fsp, unixmode);
868 unbecome_root();
869 close_file(NULL, fsp, NORMAL_CLOSE);
870 if (!newfile) {
871 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
872 FILE_NOTIFY_CHANGE_ATTRIBUTES,
873 smb_fname->base_name);
875 if (ret == 0) {
876 smb_fname->st.st_ex_mode = unixmode;
880 return( ret );
884 NTSTATUS file_set_sparse(connection_struct *conn,
885 files_struct *fsp,
886 bool sparse)
888 uint32_t old_dosmode;
889 uint32_t new_dosmode;
890 NTSTATUS status;
892 if (!CAN_WRITE(conn)) {
893 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
894 "on readonly share[%s]\n",
895 smb_fname_str_dbg(fsp->fsp_name),
896 sparse,
897 lp_servicename(talloc_tos(), SNUM(conn))));
898 return NT_STATUS_MEDIA_WRITE_PROTECTED;
901 if (!(fsp->access_mask & FILE_WRITE_DATA) &&
902 !(fsp->access_mask & FILE_WRITE_ATTRIBUTES)) {
903 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
904 "access_mask[0x%08X] - access denied\n",
905 smb_fname_str_dbg(fsp->fsp_name),
906 sparse,
907 fsp->access_mask));
908 return NT_STATUS_ACCESS_DENIED;
911 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
912 sparse, smb_fname_str_dbg(fsp->fsp_name)));
914 if (!lp_store_dos_attributes(SNUM(conn))) {
915 return NT_STATUS_INVALID_DEVICE_REQUEST;
918 status = vfs_stat_fsp(fsp);
919 if (!NT_STATUS_IS_OK(status)) {
920 return status;
923 old_dosmode = dos_mode(conn, fsp->fsp_name);
925 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
926 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
927 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
928 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
929 } else {
930 return NT_STATUS_OK;
933 /* Store the DOS attributes in an EA. */
934 if (!set_ea_dos_attribute(conn, fsp->fsp_name,
935 new_dosmode)) {
936 if (errno == 0) {
937 errno = EIO;
939 return map_nt_error_from_unix(errno);
942 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
943 FILE_NOTIFY_CHANGE_ATTRIBUTES,
944 fsp->fsp_name->base_name);
946 fsp->is_sparse = sparse;
948 return NT_STATUS_OK;
951 /*******************************************************************
952 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
953 than POSIX.
954 *******************************************************************/
956 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
957 struct smb_file_time *ft)
959 int ret = -1;
961 errno = 0;
963 DEBUG(6, ("file_ntime: actime: %s",
964 time_to_asc(convert_timespec_to_time_t(ft->atime))));
965 DEBUG(6, ("file_ntime: modtime: %s",
966 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
967 DEBUG(6, ("file_ntime: ctime: %s",
968 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
969 DEBUG(6, ("file_ntime: createtime: %s",
970 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
972 /* Don't update the time on read-only shares */
973 /* We need this as set_filetime (which can be called on
974 close and other paths) can end up calling this function
975 without the NEED_WRITE protection. Found by :
976 Leo Weppelman <leo@wau.mis.ah.nl>
979 if (!CAN_WRITE(conn)) {
980 return 0;
983 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
984 return 0;
987 if((errno != EPERM) && (errno != EACCES)) {
988 return -1;
991 if(!lp_dos_filetimes(SNUM(conn))) {
992 return -1;
995 /* We have permission (given by the Samba admin) to
996 break POSIX semantics and allow a user to change
997 the time on a file they don't own but can write to
998 (as DOS does).
1001 /* Check if we have write access. */
1002 if (can_write_to_file(conn, smb_fname)) {
1003 /* We are allowed to become root and change the filetime. */
1004 become_root();
1005 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1006 unbecome_root();
1009 return ret;
1012 /******************************************************************
1013 Force a "sticky" write time on a pathname. This will always be
1014 returned on all future write time queries and set on close.
1015 ******************************************************************/
1017 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1019 if (null_timespec(mtime)) {
1020 return true;
1023 if (!set_sticky_write_time(fileid, mtime)) {
1024 return false;
1027 return true;
1030 /******************************************************************
1031 Force a "sticky" write time on an fsp. This will always be
1032 returned on all future write time queries and set on close.
1033 ******************************************************************/
1035 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1037 if (null_timespec(mtime)) {
1038 return true;
1041 fsp->write_time_forced = true;
1042 TALLOC_FREE(fsp->update_write_time_event);
1044 return set_sticky_write_time_path(fsp->file_id, mtime);
1047 /******************************************************************
1048 Set a create time EA.
1049 ******************************************************************/
1051 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1052 const struct smb_filename *psmb_fname,
1053 struct timespec create_time)
1055 NTSTATUS status;
1056 struct smb_filename *smb_fname = NULL;
1057 uint32_t dosmode;
1058 int ret;
1060 if (!lp_store_dos_attributes(SNUM(conn))) {
1061 return NT_STATUS_OK;
1064 status = create_synthetic_smb_fname(talloc_tos(),
1065 psmb_fname->base_name,
1066 NULL, &psmb_fname->st,
1067 &smb_fname);
1069 if (!NT_STATUS_IS_OK(status)) {
1070 return status;
1073 dosmode = dos_mode(conn, smb_fname);
1075 smb_fname->st.st_ex_btime = create_time;
1077 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1078 if (ret == -1) {
1079 map_nt_error_from_unix(errno);
1082 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1083 smb_fname_str_dbg(smb_fname)));
1085 return NT_STATUS_OK;
1088 /******************************************************************
1089 Return a create time.
1090 ******************************************************************/
1092 struct timespec get_create_timespec(connection_struct *conn,
1093 struct files_struct *fsp,
1094 const struct smb_filename *smb_fname)
1096 return smb_fname->st.st_ex_btime;
1099 /******************************************************************
1100 Return a change time (may look at EA in future).
1101 ******************************************************************/
1103 struct timespec get_change_timespec(connection_struct *conn,
1104 struct files_struct *fsp,
1105 const struct smb_filename *smb_fname)
1107 return smb_fname->st.st_ex_mtime;