smbd: split out dos_mode debug print function
[Samba.git] / source3 / smbd / dosmode.c
blob93bc834917314d572049cd0eb36fab3fd7d27c35
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 void dos_mode_debug_print(uint32_t mode)
30 DEBUG(8,("dos_mode returning "));
32 if (mode & FILE_ATTRIBUTE_HIDDEN) {
33 DEBUG(8, ("h"));
35 if (mode & FILE_ATTRIBUTE_READONLY) {
36 DEBUG(8, ("r"));
38 if (mode & FILE_ATTRIBUTE_SYSTEM) {
39 DEBUG(8, ("s"));
41 if (mode & FILE_ATTRIBUTE_DIRECTORY) {
42 DEBUG(8, ("d"));
44 if (mode & FILE_ATTRIBUTE_ARCHIVE) {
45 DEBUG(8, ("a"));
47 if (mode & FILE_ATTRIBUTE_SPARSE) {
48 DEBUG(8, ("[sparse]"));
50 if (mode & FILE_ATTRIBUTE_OFFLINE) {
51 DEBUG(8, ("[offline]"));
54 DEBUG(8,("\n"));
57 static uint32_t filter_mode_by_protocol(uint32_t mode)
59 if (get_Protocol() <= PROTOCOL_LANMAN2) {
60 DEBUG(10,("filter_mode_by_protocol: "
61 "filtering result 0x%x to 0x%x\n",
62 (unsigned int)mode,
63 (unsigned int)(mode & 0x3f) ));
64 mode &= 0x3f;
66 return mode;
69 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
71 #ifdef S_ISLNK
72 #if LINKS_READ_ONLY
73 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
74 return FILE_ATTRIBUTE_READONLY;
75 #endif
76 #endif
77 return 0;
80 /****************************************************************************
81 Change a dos mode to a unix mode.
82 Base permission for files:
83 if creating file and inheriting (i.e. parent_dir != NULL)
84 apply read/write bits from parent directory.
85 else
86 everybody gets read bit set
87 dos readonly is represented in unix by removing everyone's write bit
88 dos archive is represented in unix by the user's execute bit
89 dos system is represented in unix by the group's execute bit
90 dos hidden is represented in unix by the other's execute bit
91 if !inheriting {
92 Then apply create mask,
93 then add force bits.
95 Base permission for directories:
96 dos directory is represented in unix by unix's dir bit and the exec bit
97 if !inheriting {
98 Then apply create mask,
99 then add force bits.
101 ****************************************************************************/
103 mode_t unix_mode(connection_struct *conn, int dosmode,
104 const struct smb_filename *smb_fname,
105 const char *inherit_from_dir)
107 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
108 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
109 * inheriting. */
111 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
112 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
115 if ((inherit_from_dir != NULL) && lp_inherit_perms(SNUM(conn))) {
116 struct smb_filename *smb_fname_parent;
118 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
119 smb_fname_str_dbg(smb_fname),
120 inherit_from_dir));
122 smb_fname_parent = synthetic_smb_fname(
123 talloc_tos(), inherit_from_dir, NULL, NULL);
124 if (smb_fname_parent == NULL) {
125 DEBUG(1,("unix_mode(%s) failed, [dir %s]: No memory\n",
126 smb_fname_str_dbg(smb_fname),
127 inherit_from_dir));
128 return(0);
131 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
132 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
133 smb_fname_str_dbg(smb_fname),
134 inherit_from_dir, strerror(errno)));
135 TALLOC_FREE(smb_fname_parent);
136 return(0); /* *** shouldn't happen! *** */
139 /* Save for later - but explicitly remove setuid bit for safety. */
140 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
141 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
142 smb_fname_str_dbg(smb_fname), (int)dir_mode));
143 /* Clear "result" */
144 result = 0;
145 TALLOC_FREE(smb_fname_parent);
148 if (IS_DOS_DIR(dosmode)) {
149 /* We never make directories read only for the owner as under DOS a user
150 can always create a file in a read-only directory. */
151 result |= (S_IFDIR | S_IWUSR);
153 if (dir_mode) {
154 /* Inherit mode of parent directory. */
155 result |= dir_mode;
156 } else {
157 /* Provisionally add all 'x' bits */
158 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
160 /* Apply directory mask */
161 result &= lp_dir_mask(SNUM(conn));
162 /* Add in force bits */
163 result |= lp_force_dir_mode(SNUM(conn));
165 } else {
166 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
167 result |= S_IXUSR;
169 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
170 result |= S_IXGRP;
172 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
173 result |= S_IXOTH;
175 if (dir_mode) {
176 /* Inherit 666 component of parent directory mode */
177 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
178 } else {
179 /* Apply mode mask */
180 result &= lp_create_mask(SNUM(conn));
181 /* Add in force bits */
182 result |= lp_force_create_mode(SNUM(conn));
186 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
187 (int)result));
188 return(result);
191 /****************************************************************************
192 Change a unix mode to a dos mode.
193 ****************************************************************************/
195 static uint32 dos_mode_from_sbuf(connection_struct *conn,
196 const struct smb_filename *smb_fname)
198 int result = 0;
199 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
201 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
202 /* if we can find out if a file is immutable we should report it r/o */
203 if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
204 result |= FILE_ATTRIBUTE_READONLY;
206 #endif
207 if (ro_opts == MAP_READONLY_YES) {
208 /* Original Samba method - map inverse of user "w" bit. */
209 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
210 result |= FILE_ATTRIBUTE_READONLY;
212 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
213 /* Check actual permissions for read-only. */
214 if (!can_write_to_file(conn, smb_fname)) {
215 result |= FILE_ATTRIBUTE_READONLY;
217 } /* Else never set the readonly bit. */
219 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
220 result |= FILE_ATTRIBUTE_ARCHIVE;
222 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
223 result |= FILE_ATTRIBUTE_SYSTEM;
225 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
226 result |= FILE_ATTRIBUTE_HIDDEN;
228 if (S_ISDIR(smb_fname->st.st_ex_mode))
229 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
231 result |= set_link_read_only_flag(&smb_fname->st);
233 DEBUG(8,("dos_mode_from_sbuf returning "));
235 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
236 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
237 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
238 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
239 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
241 DEBUG(8,("\n"));
242 return result;
245 /****************************************************************************
246 Get DOS attributes from an EA.
247 This can also pull the create time into the stat struct inside smb_fname.
248 ****************************************************************************/
250 static bool get_ea_dos_attribute(connection_struct *conn,
251 struct smb_filename *smb_fname,
252 uint32 *pattr)
254 struct xattr_DOSATTRIB dosattrib;
255 enum ndr_err_code ndr_err;
256 DATA_BLOB blob;
257 ssize_t sizeret;
258 fstring attrstr;
259 uint32_t dosattr;
261 if (!lp_store_dos_attributes(SNUM(conn))) {
262 return False;
265 /* Don't reset pattr to zero as we may already have filename-based attributes we
266 need to preserve. */
268 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
269 SAMBA_XATTR_DOS_ATTRIB, attrstr,
270 sizeof(attrstr));
271 if (sizeret == -1) {
272 if (errno == ENOSYS
273 #if defined(ENOTSUP)
274 || errno == ENOTSUP) {
275 #else
277 #endif
278 DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
279 "from EA on file %s: Error = %s\n",
280 smb_fname_str_dbg(smb_fname),
281 strerror(errno)));
282 set_store_dos_attributes(SNUM(conn), False);
284 return False;
287 blob.data = (uint8_t *)attrstr;
288 blob.length = sizeret;
290 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
291 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
293 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
294 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
295 "from EA on file %s: Error = %s\n",
296 smb_fname_str_dbg(smb_fname),
297 ndr_errstr(ndr_err)));
298 return false;
301 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
302 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
304 switch (dosattrib.version) {
305 case 0xFFFF:
306 dosattr = dosattrib.info.compatinfoFFFF.attrib;
307 break;
308 case 1:
309 dosattr = dosattrib.info.info1.attrib;
310 if (!null_nttime(dosattrib.info.info1.create_time)) {
311 struct timespec create_time =
312 nt_time_to_unix_timespec(
313 &dosattrib.info.info1.create_time);
315 update_stat_ex_create_time(&smb_fname->st,
316 create_time);
318 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
319 "set btime %s\n",
320 smb_fname_str_dbg(smb_fname),
321 time_to_asc(convert_timespec_to_time_t(
322 create_time)) ));
324 break;
325 case 2:
326 dosattr = dosattrib.info.oldinfo2.attrib;
327 /* Don't know what flags to check for this case. */
328 break;
329 case 3:
330 dosattr = dosattrib.info.info3.attrib;
331 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
332 !null_nttime(dosattrib.info.info3.create_time)) {
333 struct timespec create_time =
334 nt_time_to_unix_timespec(
335 &dosattrib.info.info3.create_time);
337 update_stat_ex_create_time(&smb_fname->st,
338 create_time);
340 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
341 "set btime %s\n",
342 smb_fname_str_dbg(smb_fname),
343 time_to_asc(convert_timespec_to_time_t(
344 create_time)) ));
346 break;
347 default:
348 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
349 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
350 attrstr));
351 return false;
354 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
355 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
357 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
358 *pattr = (uint32)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
360 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
362 if (dosattr & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
363 if (dosattr & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
364 if (dosattr & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
365 if (dosattr & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
366 if (dosattr & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
368 DEBUG(8,("\n"));
370 return True;
373 /****************************************************************************
374 Set DOS attributes in an EA.
375 Also sets the create time.
376 ****************************************************************************/
378 static bool set_ea_dos_attribute(connection_struct *conn,
379 struct smb_filename *smb_fname,
380 uint32 dosmode)
382 struct xattr_DOSATTRIB dosattrib;
383 enum ndr_err_code ndr_err;
384 DATA_BLOB blob;
386 ZERO_STRUCT(dosattrib);
387 ZERO_STRUCT(blob);
389 dosattrib.version = 3;
390 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
391 XATTR_DOSINFO_CREATE_TIME;
392 dosattrib.info.info3.attrib = dosmode;
393 unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
394 smb_fname->st.st_ex_btime);
396 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
397 (unsigned int)dosmode,
398 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
399 smb_fname_str_dbg(smb_fname) ));
401 ndr_err = ndr_push_struct_blob(
402 &blob, talloc_tos(), &dosattrib,
403 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
405 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
406 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
407 ndr_errstr(ndr_err)));
408 return false;
411 if (blob.data == NULL || blob.length == 0) {
412 return false;
415 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
416 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
417 0) == -1) {
418 bool ret = false;
419 files_struct *fsp = NULL;
421 if((errno != EPERM) && (errno != EACCES)) {
422 if (errno == ENOSYS
423 #if defined(ENOTSUP)
424 || errno == ENOTSUP) {
425 #else
427 #endif
428 DEBUG(1,("set_ea_dos_attributes: Cannot set "
429 "attribute EA on file %s: Error = %s\n",
430 smb_fname_str_dbg(smb_fname),
431 strerror(errno) ));
432 set_store_dos_attributes(SNUM(conn), False);
434 return false;
437 /* We want DOS semantics, ie allow non owner with write permission to change the
438 bits on a file. Just like file_ntimes below.
441 /* Check if we have write access. */
442 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
443 return false;
445 if (!can_write_to_file(conn, smb_fname)) {
446 return false;
450 * We need to open the file with write access whilst
451 * still in our current user context. This ensures we
452 * are not violating security in doing the setxattr.
455 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
456 &fsp)))
457 return false;
458 become_root();
459 if (SMB_VFS_FSETXATTR(fsp,
460 SAMBA_XATTR_DOS_ATTRIB, blob.data,
461 blob.length, 0) == 0) {
462 ret = true;
464 unbecome_root();
465 close_file(NULL, fsp, NORMAL_CLOSE);
466 return ret;
468 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
469 (unsigned int)dosmode,
470 smb_fname_str_dbg(smb_fname)));
471 return true;
474 /****************************************************************************
475 Change a unix mode to a dos mode for an ms dfs link.
476 ****************************************************************************/
478 uint32 dos_mode_msdfs(connection_struct *conn,
479 const struct smb_filename *smb_fname)
481 uint32 result = 0;
483 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
485 if (!VALID_STAT(smb_fname->st)) {
486 return 0;
489 /* First do any modifications that depend on the path name. */
490 /* hide files with a name starting with a . */
491 if (lp_hide_dot_files(SNUM(conn))) {
492 const char *p = strrchr_m(smb_fname->base_name, '/');
493 if (p) {
494 p++;
495 } else {
496 p = smb_fname->base_name;
499 /* Only . and .. are not hidden. */
500 if (p[0] == '.' && !((p[1] == '\0') ||
501 (p[1] == '.' && p[2] == '\0'))) {
502 result |= FILE_ATTRIBUTE_HIDDEN;
506 result |= dos_mode_from_sbuf(conn, smb_fname);
508 /* Optimization : Only call is_hidden_path if it's not already
509 hidden. */
510 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
511 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
512 result |= FILE_ATTRIBUTE_HIDDEN;
515 if (result == 0) {
516 result = FILE_ATTRIBUTE_NORMAL;
519 result = filter_mode_by_protocol(result);
522 * Add in that it is a reparse point
524 result |= FILE_ATTRIBUTE_REPARSE_POINT;
526 DEBUG(8,("dos_mode_msdfs returning "));
528 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
529 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
530 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
531 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
532 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
533 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
535 DEBUG(8,("\n"));
537 return(result);
540 #ifdef HAVE_STAT_DOS_FLAGS
541 /****************************************************************************
542 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
543 ****************************************************************************/
545 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
547 uint32_t dos_stat_flags = 0;
549 if (dosmode & FILE_ATTRIBUTE_ARCHIVE)
550 dos_stat_flags |= UF_DOS_ARCHIVE;
551 if (dosmode & FILE_ATTRIBUTE_HIDDEN)
552 dos_stat_flags |= UF_DOS_HIDDEN;
553 if (dosmode & FILE_ATTRIBUTE_READONLY)
554 dos_stat_flags |= UF_DOS_RO;
555 if (dosmode & FILE_ATTRIBUTE_SYSTEM)
556 dos_stat_flags |= UF_DOS_SYSTEM;
557 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
558 dos_stat_flags |= UF_DOS_NOINDEX;
560 return dos_stat_flags;
563 /****************************************************************************
564 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
565 ****************************************************************************/
567 static bool get_stat_dos_flags(connection_struct *conn,
568 const struct smb_filename *smb_fname,
569 uint32_t *dosmode)
571 SMB_ASSERT(VALID_STAT(smb_fname->st));
572 SMB_ASSERT(dosmode);
574 if (!lp_store_dos_attributes(SNUM(conn))) {
575 return false;
578 DEBUG(5, ("Getting stat dos attributes for %s.\n",
579 smb_fname_str_dbg(smb_fname)));
581 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
582 *dosmode |= FILE_ATTRIBUTE_ARCHIVE;
583 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
584 *dosmode |= FILE_ATTRIBUTE_HIDDEN;
585 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
586 *dosmode |= FILE_ATTRIBUTE_READONLY;
587 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
588 *dosmode |= FILE_ATTRIBUTE_SYSTEM;
589 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
590 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
591 if (smb_fname->st.st_ex_flags & FILE_ATTRIBUTE_SPARSE)
592 *dosmode |= FILE_ATTRIBUTE_SPARSE;
593 if (S_ISDIR(smb_fname->st.st_ex_mode))
594 *dosmode |= FILE_ATTRIBUTE_DIRECTORY;
596 *dosmode |= set_link_read_only_flag(&smb_fname->st);
598 return true;
601 /****************************************************************************
602 Sets DOS attributes, stored in st_ex_flags of the inode.
603 ****************************************************************************/
605 static bool set_stat_dos_flags(connection_struct *conn,
606 const struct smb_filename *smb_fname,
607 uint32_t dosmode,
608 bool *attributes_changed)
610 uint32_t new_flags = 0;
611 int error = 0;
613 SMB_ASSERT(VALID_STAT(smb_fname->st));
614 SMB_ASSERT(attributes_changed);
616 *attributes_changed = false;
618 if (!lp_store_dos_attributes(SNUM(conn))) {
619 return false;
622 DEBUG(5, ("Setting stat dos attributes for %s.\n",
623 smb_fname_str_dbg(smb_fname)));
625 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
626 dos_attributes_to_stat_dos_flags(dosmode);
628 /* Return early if no flags changed. */
629 if (new_flags == smb_fname->st.st_ex_flags)
630 return true;
632 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
633 smb_fname->st.st_ex_flags));
635 /* Set new flags with chflags. */
636 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
637 if (error) {
638 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
639 "file %s! errno=%d\n", new_flags,
640 smb_fname_str_dbg(smb_fname), errno));
641 return false;
644 *attributes_changed = true;
645 return true;
647 #endif /* HAVE_STAT_DOS_FLAGS */
649 /****************************************************************************
650 Change a unix mode to a dos mode.
651 May also read the create timespec into the stat struct in smb_fname
652 if "store dos attributes" is true.
653 ****************************************************************************/
655 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
657 uint32 result = 0;
658 bool offline, used_stat_dos_flags = false;
660 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
662 if (!VALID_STAT(smb_fname->st)) {
663 return 0;
666 /* First do any modifications that depend on the path name. */
667 /* hide files with a name starting with a . */
668 if (lp_hide_dot_files(SNUM(conn))) {
669 const char *p = strrchr_m(smb_fname->base_name,'/');
670 if (p) {
671 p++;
672 } else {
673 p = smb_fname->base_name;
676 /* Only . and .. are not hidden. */
677 if (p[0] == '.' && !((p[1] == '\0') ||
678 (p[1] == '.' && p[2] == '\0'))) {
679 result |= FILE_ATTRIBUTE_HIDDEN;
683 #ifdef HAVE_STAT_DOS_FLAGS
684 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
685 #endif
686 if (!used_stat_dos_flags) {
687 /* Get the DOS attributes from an EA by preference. */
688 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
689 result |= dos_mode_from_sbuf(conn, smb_fname);
693 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
694 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
695 result |= FILE_ATTRIBUTE_OFFLINE;
698 /* Optimization : Only call is_hidden_path if it's not already
699 hidden. */
700 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
701 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
702 result |= FILE_ATTRIBUTE_HIDDEN;
705 if (result == 0) {
706 result = FILE_ATTRIBUTE_NORMAL;
709 result = filter_mode_by_protocol(result);
711 dos_mode_debug_print(result);
713 return result;
716 /*******************************************************************
717 chmod a file - but preserve some bits.
718 If "store dos attributes" is also set it will store the create time
719 from the stat struct in smb_fname (in NTTIME format) in the EA
720 attribute also.
721 ********************************************************************/
723 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
724 uint32 dosmode, const char *parent_dir, bool newfile)
726 int mask=0;
727 mode_t tmp;
728 mode_t unixmode;
729 int ret = -1, lret = -1;
730 uint32_t old_mode;
731 struct timespec new_create_timespec;
732 files_struct *fsp = NULL;
734 if (!CAN_WRITE(conn)) {
735 errno = EROFS;
736 return -1;
739 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
740 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
742 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
743 dosmode, smb_fname_str_dbg(smb_fname)));
745 unixmode = smb_fname->st.st_ex_mode;
747 get_acl_group_bits(conn, smb_fname->base_name,
748 &smb_fname->st.st_ex_mode);
750 if (S_ISDIR(smb_fname->st.st_ex_mode))
751 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
752 else
753 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
755 new_create_timespec = smb_fname->st.st_ex_btime;
757 old_mode = dos_mode(conn, smb_fname);
759 if ((dosmode & FILE_ATTRIBUTE_OFFLINE) &&
760 !(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
761 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
762 if (lret == -1) {
763 if (errno == ENOTSUP) {
764 DEBUG(10, ("Setting FILE_ATTRIBUTE_OFFLINE for "
765 "%s/%s is not supported.\n",
766 parent_dir,
767 smb_fname_str_dbg(smb_fname)));
768 } else {
769 DEBUG(0, ("An error occurred while setting "
770 "FILE_ATTRIBUTE_OFFLINE for "
771 "%s/%s: %s", parent_dir,
772 smb_fname_str_dbg(smb_fname),
773 strerror(errno)));
778 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
779 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
781 smb_fname->st.st_ex_btime = new_create_timespec;
783 #ifdef HAVE_STAT_DOS_FLAGS
785 bool attributes_changed;
787 if (set_stat_dos_flags(conn, smb_fname, dosmode,
788 &attributes_changed))
790 if (!newfile && attributes_changed) {
791 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
792 FILE_NOTIFY_CHANGE_ATTRIBUTES,
793 smb_fname->base_name);
795 smb_fname->st.st_ex_mode = unixmode;
796 return 0;
799 #endif
800 /* Store the DOS attributes in an EA by preference. */
801 if (lp_store_dos_attributes(SNUM(conn))) {
803 * Don't fall back to using UNIX modes. Finally
804 * follow the smb.conf manpage.
806 if (!set_ea_dos_attribute(conn, smb_fname, dosmode)) {
807 return -1;
809 if (!newfile) {
810 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
811 FILE_NOTIFY_CHANGE_ATTRIBUTES,
812 smb_fname->base_name);
814 smb_fname->st.st_ex_mode = unixmode;
815 return 0;
818 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
820 /* preserve the file type bits */
821 mask |= S_IFMT;
823 /* preserve the s bits */
824 mask |= (S_ISUID | S_ISGID);
826 /* preserve the t bit */
827 #ifdef S_ISVTX
828 mask |= S_ISVTX;
829 #endif
831 /* possibly preserve the x bits */
832 if (!MAP_ARCHIVE(conn))
833 mask |= S_IXUSR;
834 if (!MAP_SYSTEM(conn))
835 mask |= S_IXGRP;
836 if (!MAP_HIDDEN(conn))
837 mask |= S_IXOTH;
839 unixmode |= (smb_fname->st.st_ex_mode & mask);
841 /* if we previously had any r bits set then leave them alone */
842 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
843 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
844 unixmode |= tmp;
847 /* if we previously had any w bits set then leave them alone
848 whilst adding in the new w bits, if the new mode is not rdonly */
849 if (!IS_DOS_READONLY(dosmode)) {
850 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
854 * From the chmod 2 man page:
856 * "If the calling process is not privileged, and the group of the file
857 * does not match the effective group ID of the process or one of its
858 * supplementary group IDs, the S_ISGID bit will be turned off, but
859 * this will not cause an error to be returned."
861 * Simply refuse to do the chmod in this case.
864 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
865 geteuid() != sec_initial_uid() &&
866 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
867 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
868 "set for directory %s\n",
869 smb_fname_str_dbg(smb_fname)));
870 errno = EPERM;
871 return -1;
874 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
875 if (ret == 0) {
876 if(!newfile || (lret != -1)) {
877 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
878 FILE_NOTIFY_CHANGE_ATTRIBUTES,
879 smb_fname->base_name);
881 smb_fname->st.st_ex_mode = unixmode;
882 return 0;
885 if((errno != EPERM) && (errno != EACCES))
886 return -1;
888 if(!lp_dos_filemode(SNUM(conn)))
889 return -1;
891 /* We want DOS semantics, ie allow non owner with write permission to change the
892 bits on a file. Just like file_ntimes below.
895 if (!can_write_to_file(conn, smb_fname)) {
896 errno = EACCES;
897 return -1;
901 * We need to open the file with write access whilst
902 * still in our current user context. This ensures we
903 * are not violating security in doing the fchmod.
905 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
906 &fsp)))
907 return -1;
908 become_root();
909 ret = SMB_VFS_FCHMOD(fsp, unixmode);
910 unbecome_root();
911 close_file(NULL, fsp, NORMAL_CLOSE);
912 if (!newfile) {
913 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
914 FILE_NOTIFY_CHANGE_ATTRIBUTES,
915 smb_fname->base_name);
917 if (ret == 0) {
918 smb_fname->st.st_ex_mode = unixmode;
921 return( ret );
925 NTSTATUS file_set_sparse(connection_struct *conn,
926 files_struct *fsp,
927 bool sparse)
929 uint32_t old_dosmode;
930 uint32_t new_dosmode;
931 NTSTATUS status;
933 if (!CAN_WRITE(conn)) {
934 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
935 "on readonly share[%s]\n",
936 smb_fname_str_dbg(fsp->fsp_name),
937 sparse,
938 lp_servicename(talloc_tos(), SNUM(conn))));
939 return NT_STATUS_MEDIA_WRITE_PROTECTED;
942 if (!(fsp->access_mask & FILE_WRITE_DATA) &&
943 !(fsp->access_mask & FILE_WRITE_ATTRIBUTES)) {
944 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
945 "access_mask[0x%08X] - access denied\n",
946 smb_fname_str_dbg(fsp->fsp_name),
947 sparse,
948 fsp->access_mask));
949 return NT_STATUS_ACCESS_DENIED;
952 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
953 sparse, smb_fname_str_dbg(fsp->fsp_name)));
955 if (!lp_store_dos_attributes(SNUM(conn))) {
956 return NT_STATUS_INVALID_DEVICE_REQUEST;
959 status = vfs_stat_fsp(fsp);
960 if (!NT_STATUS_IS_OK(status)) {
961 return status;
964 old_dosmode = dos_mode(conn, fsp->fsp_name);
966 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
967 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
968 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
969 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
970 } else {
971 return NT_STATUS_OK;
974 /* Store the DOS attributes in an EA. */
975 if (!set_ea_dos_attribute(conn, fsp->fsp_name,
976 new_dosmode)) {
977 if (errno == 0) {
978 errno = EIO;
980 return map_nt_error_from_unix(errno);
983 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
984 FILE_NOTIFY_CHANGE_ATTRIBUTES,
985 fsp->fsp_name->base_name);
987 fsp->is_sparse = sparse;
989 return NT_STATUS_OK;
992 /*******************************************************************
993 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
994 than POSIX.
995 *******************************************************************/
997 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
998 struct smb_file_time *ft)
1000 int ret = -1;
1002 errno = 0;
1004 DEBUG(6, ("file_ntime: actime: %s",
1005 time_to_asc(convert_timespec_to_time_t(ft->atime))));
1006 DEBUG(6, ("file_ntime: modtime: %s",
1007 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
1008 DEBUG(6, ("file_ntime: ctime: %s",
1009 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
1010 DEBUG(6, ("file_ntime: createtime: %s",
1011 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
1013 /* Don't update the time on read-only shares */
1014 /* We need this as set_filetime (which can be called on
1015 close and other paths) can end up calling this function
1016 without the NEED_WRITE protection. Found by :
1017 Leo Weppelman <leo@wau.mis.ah.nl>
1020 if (!CAN_WRITE(conn)) {
1021 return 0;
1024 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
1025 return 0;
1028 if((errno != EPERM) && (errno != EACCES)) {
1029 return -1;
1032 if(!lp_dos_filetimes(SNUM(conn))) {
1033 return -1;
1036 /* We have permission (given by the Samba admin) to
1037 break POSIX semantics and allow a user to change
1038 the time on a file they don't own but can write to
1039 (as DOS does).
1042 /* Check if we have write access. */
1043 if (can_write_to_file(conn, smb_fname)) {
1044 /* We are allowed to become root and change the filetime. */
1045 become_root();
1046 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1047 unbecome_root();
1050 return ret;
1053 /******************************************************************
1054 Force a "sticky" write time on a pathname. This will always be
1055 returned on all future write time queries and set on close.
1056 ******************************************************************/
1058 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1060 if (null_timespec(mtime)) {
1061 return true;
1064 if (!set_sticky_write_time(fileid, mtime)) {
1065 return false;
1068 return true;
1071 /******************************************************************
1072 Force a "sticky" write time on an fsp. This will always be
1073 returned on all future write time queries and set on close.
1074 ******************************************************************/
1076 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1078 if (null_timespec(mtime)) {
1079 return true;
1082 fsp->write_time_forced = true;
1083 TALLOC_FREE(fsp->update_write_time_event);
1085 return set_sticky_write_time_path(fsp->file_id, mtime);
1088 /******************************************************************
1089 Set a create time EA.
1090 ******************************************************************/
1092 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1093 const struct smb_filename *psmb_fname,
1094 struct timespec create_time)
1096 struct smb_filename *smb_fname;
1097 uint32_t dosmode;
1098 int ret;
1100 if (!lp_store_dos_attributes(SNUM(conn))) {
1101 return NT_STATUS_OK;
1104 smb_fname = synthetic_smb_fname(talloc_tos(), psmb_fname->base_name,
1105 NULL, &psmb_fname->st);
1107 if (smb_fname == NULL) {
1108 return NT_STATUS_NO_MEMORY;
1111 dosmode = dos_mode(conn, smb_fname);
1113 smb_fname->st.st_ex_btime = create_time;
1115 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1116 if (ret == -1) {
1117 map_nt_error_from_unix(errno);
1120 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1121 smb_fname_str_dbg(smb_fname)));
1123 return NT_STATUS_OK;
1126 /******************************************************************
1127 Return a create time.
1128 ******************************************************************/
1130 struct timespec get_create_timespec(connection_struct *conn,
1131 struct files_struct *fsp,
1132 const struct smb_filename *smb_fname)
1134 return smb_fname->st.st_ex_btime;
1137 /******************************************************************
1138 Return a change time (may look at EA in future).
1139 ******************************************************************/
1141 struct timespec get_change_timespec(connection_struct *conn,
1142 struct files_struct *fsp,
1143 const struct smb_filename *smb_fname)
1145 return smb_fname->st.st_ex_mtime;