Use new common function.
[Samba/gebeck_regimport.git] / source3 / smbd / dosmode.c
blob747ba2169a03eeea0b2931cb5d364c35db7b53ae
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"
27 static uint32_t filter_mode_by_protocol(uint32_t mode)
29 if (get_Protocol() <= PROTOCOL_LANMAN2) {
30 DEBUG(10,("filter_mode_by_protocol: "
31 "filtering result 0x%x to 0x%x\n",
32 (unsigned int)mode,
33 (unsigned int)(mode & 0x3f) ));
34 mode &= 0x3f;
36 return mode;
39 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
41 #ifdef S_ISLNK
42 #if LINKS_READ_ONLY
43 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
44 return FILE_ATTRIBUTE_READONLY;
45 #endif
46 #endif
47 return 0;
50 /****************************************************************************
51 Change a dos mode to a unix mode.
52 Base permission for files:
53 if creating file and inheriting (i.e. parent_dir != NULL)
54 apply read/write bits from parent directory.
55 else
56 everybody gets read bit set
57 dos readonly is represented in unix by removing everyone's write bit
58 dos archive is represented in unix by the user's execute bit
59 dos system is represented in unix by the group's execute bit
60 dos hidden is represented in unix by the other's execute bit
61 if !inheriting {
62 Then apply create mask,
63 then add force bits.
65 Base permission for directories:
66 dos directory is represented in unix by unix's dir bit and the exec bit
67 if !inheriting {
68 Then apply create mask,
69 then add force bits.
71 ****************************************************************************/
73 mode_t unix_mode(connection_struct *conn, int dosmode,
74 const struct smb_filename *smb_fname,
75 const char *inherit_from_dir)
77 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
78 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
79 * inheriting. */
81 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
82 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
85 if ((inherit_from_dir != NULL) && lp_inherit_perms(SNUM(conn))) {
86 struct smb_filename *smb_fname_parent = NULL;
87 NTSTATUS status;
89 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
90 smb_fname_str_dbg(smb_fname),
91 inherit_from_dir));
93 status = create_synthetic_smb_fname(talloc_tos(),
94 inherit_from_dir, NULL,
95 NULL, &smb_fname_parent);
96 if (!NT_STATUS_IS_OK(status)) {
97 DEBUG(1,("unix_mode(%s) failed, [dir %s]: %s\n",
98 smb_fname_str_dbg(smb_fname),
99 inherit_from_dir, nt_errstr(status)));
100 return(0);
103 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
104 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
105 smb_fname_str_dbg(smb_fname),
106 inherit_from_dir, strerror(errno)));
107 TALLOC_FREE(smb_fname_parent);
108 return(0); /* *** shouldn't happen! *** */
111 /* Save for later - but explicitly remove setuid bit for safety. */
112 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
113 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
114 smb_fname_str_dbg(smb_fname), (int)dir_mode));
115 /* Clear "result" */
116 result = 0;
117 TALLOC_FREE(smb_fname_parent);
120 if (IS_DOS_DIR(dosmode)) {
121 /* We never make directories read only for the owner as under DOS a user
122 can always create a file in a read-only directory. */
123 result |= (S_IFDIR | S_IWUSR);
125 if (dir_mode) {
126 /* Inherit mode of parent directory. */
127 result |= dir_mode;
128 } else {
129 /* Provisionally add all 'x' bits */
130 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
132 /* Apply directory mask */
133 result &= lp_dir_mask(SNUM(conn));
134 /* Add in force bits */
135 result |= lp_force_dir_mode(SNUM(conn));
137 } else {
138 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
139 result |= S_IXUSR;
141 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
142 result |= S_IXGRP;
144 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
145 result |= S_IXOTH;
147 if (dir_mode) {
148 /* Inherit 666 component of parent directory mode */
149 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
150 } else {
151 /* Apply mode mask */
152 result &= lp_create_mask(SNUM(conn));
153 /* Add in force bits */
154 result |= lp_force_create_mode(SNUM(conn));
158 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
159 (int)result));
160 return(result);
163 /****************************************************************************
164 Change a unix mode to a dos mode.
165 ****************************************************************************/
167 static uint32 dos_mode_from_sbuf(connection_struct *conn,
168 const struct smb_filename *smb_fname)
170 int result = 0;
171 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
173 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
174 /* if we can find out if a file is immutable we should report it r/o */
175 if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
176 result |= FILE_ATTRIBUTE_READONLY;
178 #endif
179 if (ro_opts == MAP_READONLY_YES) {
180 /* Original Samba method - map inverse of user "w" bit. */
181 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
182 result |= FILE_ATTRIBUTE_READONLY;
184 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
185 /* Check actual permissions for read-only. */
186 if (!can_write_to_file(conn, smb_fname)) {
187 result |= FILE_ATTRIBUTE_READONLY;
189 } /* Else never set the readonly bit. */
191 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
192 result |= FILE_ATTRIBUTE_ARCHIVE;
194 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
195 result |= FILE_ATTRIBUTE_SYSTEM;
197 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
198 result |= FILE_ATTRIBUTE_HIDDEN;
200 if (S_ISDIR(smb_fname->st.st_ex_mode))
201 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
203 result |= set_link_read_only_flag(&smb_fname->st);
205 DEBUG(8,("dos_mode_from_sbuf returning "));
207 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
208 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
209 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
210 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
211 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
213 DEBUG(8,("\n"));
214 return result;
217 /****************************************************************************
218 Get DOS attributes from an EA.
219 This can also pull the create time into the stat struct inside smb_fname.
220 ****************************************************************************/
222 static bool get_ea_dos_attribute(connection_struct *conn,
223 struct smb_filename *smb_fname,
224 uint32 *pattr)
226 struct xattr_DOSATTRIB dosattrib;
227 enum ndr_err_code ndr_err;
228 DATA_BLOB blob;
229 ssize_t sizeret;
230 fstring attrstr;
231 uint32_t dosattr;
233 if (!lp_store_dos_attributes(SNUM(conn))) {
234 return False;
237 /* Don't reset pattr to zero as we may already have filename-based attributes we
238 need to preserve. */
240 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
241 SAMBA_XATTR_DOS_ATTRIB, attrstr,
242 sizeof(attrstr));
243 if (sizeret == -1) {
244 if (errno == ENOSYS
245 #if defined(ENOTSUP)
246 || errno == ENOTSUP) {
247 #else
249 #endif
250 DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
251 "from EA on file %s: Error = %s\n",
252 smb_fname_str_dbg(smb_fname),
253 strerror(errno)));
254 set_store_dos_attributes(SNUM(conn), False);
256 return False;
259 blob.data = (uint8_t *)attrstr;
260 blob.length = sizeret;
262 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
263 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
265 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
266 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
267 "from EA on file %s: Error = %s\n",
268 smb_fname_str_dbg(smb_fname),
269 ndr_errstr(ndr_err)));
270 return false;
273 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
274 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
276 switch (dosattrib.version) {
277 case 0xFFFF:
278 dosattr = dosattrib.info.compatinfoFFFF.attrib;
279 break;
280 case 1:
281 dosattr = dosattrib.info.info1.attrib;
282 if (!null_nttime(dosattrib.info.info1.create_time)) {
283 struct timespec create_time =
284 nt_time_to_unix_timespec(
285 &dosattrib.info.info1.create_time);
287 update_stat_ex_create_time(&smb_fname->st,
288 create_time);
290 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
291 "set btime %s\n",
292 smb_fname_str_dbg(smb_fname),
293 time_to_asc(convert_timespec_to_time_t(
294 create_time)) ));
296 break;
297 case 2:
298 dosattr = dosattrib.info.oldinfo2.attrib;
299 /* Don't know what flags to check for this case. */
300 break;
301 case 3:
302 dosattr = dosattrib.info.info3.attrib;
303 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
304 !null_nttime(dosattrib.info.info3.create_time)) {
305 struct timespec create_time =
306 nt_time_to_unix_timespec(
307 &dosattrib.info.info3.create_time);
309 update_stat_ex_create_time(&smb_fname->st,
310 create_time);
312 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
313 "set btime %s\n",
314 smb_fname_str_dbg(smb_fname),
315 time_to_asc(convert_timespec_to_time_t(
316 create_time)) ));
318 break;
319 default:
320 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
321 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
322 attrstr));
323 return false;
326 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
327 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
329 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
330 *pattr = (uint32)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
332 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
334 if (dosattr & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
335 if (dosattr & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
336 if (dosattr & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
337 if (dosattr & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
338 if (dosattr & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
340 DEBUG(8,("\n"));
342 return True;
345 /****************************************************************************
346 Set DOS attributes in an EA.
347 Also sets the create time.
348 ****************************************************************************/
350 static bool set_ea_dos_attribute(connection_struct *conn,
351 struct smb_filename *smb_fname,
352 uint32 dosmode)
354 struct xattr_DOSATTRIB dosattrib;
355 enum ndr_err_code ndr_err;
356 DATA_BLOB blob;
358 if (!lp_store_dos_attributes(SNUM(conn))) {
359 return False;
362 ZERO_STRUCT(dosattrib);
363 ZERO_STRUCT(blob);
365 dosattrib.version = 3;
366 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
367 XATTR_DOSINFO_CREATE_TIME;
368 dosattrib.info.info3.attrib = dosmode;
369 unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
370 smb_fname->st.st_ex_btime);
372 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
373 (unsigned int)dosmode,
374 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
375 smb_fname_str_dbg(smb_fname) ));
377 ndr_err = ndr_push_struct_blob(
378 &blob, talloc_tos(), &dosattrib,
379 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
381 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
382 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
383 ndr_errstr(ndr_err)));
384 return false;
387 if (blob.data == NULL || blob.length == 0) {
388 return false;
391 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
392 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
393 0) == -1) {
394 bool ret = false;
395 files_struct *fsp = NULL;
397 if((errno != EPERM) && (errno != EACCES)) {
398 if (errno == ENOSYS
399 #if defined(ENOTSUP)
400 || errno == ENOTSUP) {
401 #else
403 #endif
404 DEBUG(1,("set_ea_dos_attributes: Cannot set "
405 "attribute EA on file %s: Error = %s\n",
406 smb_fname_str_dbg(smb_fname),
407 strerror(errno) ));
408 set_store_dos_attributes(SNUM(conn), False);
410 return false;
413 /* We want DOS semantics, ie allow non owner with write permission to change the
414 bits on a file. Just like file_ntimes below.
417 /* Check if we have write access. */
418 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
419 return false;
422 * We need to open the file with write access whilst
423 * still in our current user context. This ensures we
424 * are not violating security in doing the setxattr.
427 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
428 &fsp)))
429 return false;
430 become_root();
431 if (SMB_VFS_FSETXATTR(fsp,
432 SAMBA_XATTR_DOS_ATTRIB, blob.data,
433 blob.length, 0) == 0) {
434 ret = true;
436 unbecome_root();
437 close_file(NULL, fsp, NORMAL_CLOSE);
438 return ret;
440 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
441 (unsigned int)dosmode,
442 smb_fname_str_dbg(smb_fname)));
443 return true;
446 /****************************************************************************
447 Change a unix mode to a dos mode for an ms dfs link.
448 ****************************************************************************/
450 uint32 dos_mode_msdfs(connection_struct *conn,
451 const struct smb_filename *smb_fname)
453 uint32 result = 0;
455 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
457 if (!VALID_STAT(smb_fname->st)) {
458 return 0;
461 /* First do any modifications that depend on the path name. */
462 /* hide files with a name starting with a . */
463 if (lp_hide_dot_files(SNUM(conn))) {
464 const char *p = strrchr_m(smb_fname->base_name, '/');
465 if (p) {
466 p++;
467 } else {
468 p = smb_fname->base_name;
471 /* Only . and .. are not hidden. */
472 if (p[0] == '.' && !((p[1] == '\0') ||
473 (p[1] == '.' && p[2] == '\0'))) {
474 result |= FILE_ATTRIBUTE_HIDDEN;
478 result |= dos_mode_from_sbuf(conn, smb_fname);
480 /* Optimization : Only call is_hidden_path if it's not already
481 hidden. */
482 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
483 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
484 result |= FILE_ATTRIBUTE_HIDDEN;
487 if (result == 0) {
488 result = FILE_ATTRIBUTE_NORMAL;
491 result = filter_mode_by_protocol(result);
493 DEBUG(8,("dos_mode_msdfs returning "));
495 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
496 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
497 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
498 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
499 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
500 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
502 DEBUG(8,("\n"));
504 return(result);
507 #ifdef HAVE_STAT_DOS_FLAGS
508 /****************************************************************************
509 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
510 ****************************************************************************/
512 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
514 uint32_t dos_stat_flags = 0;
516 if (dosmode & FILE_ATTRIBUTE_ARCHIVE)
517 dos_stat_flags |= UF_DOS_ARCHIVE;
518 if (dosmode & FILE_ATTRIBUTE_HIDDEN)
519 dos_stat_flags |= UF_DOS_HIDDEN;
520 if (dosmode & FILE_ATTRIBUTE_READONLY)
521 dos_stat_flags |= UF_DOS_RO;
522 if (dosmode & FILE_ATTRIBUTE_SYSTEM)
523 dos_stat_flags |= UF_DOS_SYSTEM;
524 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
525 dos_stat_flags |= UF_DOS_NOINDEX;
527 return dos_stat_flags;
530 /****************************************************************************
531 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
532 ****************************************************************************/
534 static bool get_stat_dos_flags(connection_struct *conn,
535 const struct smb_filename *smb_fname,
536 uint32_t *dosmode)
538 SMB_ASSERT(VALID_STAT(smb_fname->st));
539 SMB_ASSERT(dosmode);
541 if (!lp_store_dos_attributes(SNUM(conn))) {
542 return false;
545 DEBUG(5, ("Getting stat dos attributes for %s.\n",
546 smb_fname_str_dbg(smb_fname)));
548 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
549 *dosmode |= FILE_ATTRIBUTE_ARCHIVE;
550 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
551 *dosmode |= FILE_ATTRIBUTE_HIDDEN;
552 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
553 *dosmode |= FILE_ATTRIBUTE_READONLY;
554 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
555 *dosmode |= FILE_ATTRIBUTE_SYSTEM;
556 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
557 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
558 if (smb_fname->st.st_ex_flags & FILE_ATTRIBUTE_SPARSE)
559 *dosmode |= FILE_ATTRIBUTE_SPARSE;
560 if (S_ISDIR(smb_fname->st.st_ex_mode))
561 *dosmode |= FILE_ATTRIBUTE_DIRECTORY;
563 *dosmode |= set_link_read_only_flag(&smb_fname->st);
565 return true;
568 /****************************************************************************
569 Sets DOS attributes, stored in st_ex_flags of the inode.
570 ****************************************************************************/
572 static bool set_stat_dos_flags(connection_struct *conn,
573 const struct smb_filename *smb_fname,
574 uint32_t dosmode,
575 bool *attributes_changed)
577 uint32_t new_flags = 0;
578 int error = 0;
580 SMB_ASSERT(VALID_STAT(smb_fname->st));
581 SMB_ASSERT(attributes_changed);
583 *attributes_changed = false;
585 if (!lp_store_dos_attributes(SNUM(conn))) {
586 return false;
589 DEBUG(5, ("Setting stat dos attributes for %s.\n",
590 smb_fname_str_dbg(smb_fname)));
592 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
593 dos_attributes_to_stat_dos_flags(dosmode);
595 /* Return early if no flags changed. */
596 if (new_flags == smb_fname->st.st_ex_flags)
597 return true;
599 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
600 smb_fname->st.st_ex_flags));
602 /* Set new flags with chflags. */
603 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
604 if (error) {
605 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
606 "file %s! errno=%d\n", new_flags,
607 smb_fname_str_dbg(smb_fname), errno));
608 return false;
611 *attributes_changed = true;
612 return true;
614 #endif /* HAVE_STAT_DOS_FLAGS */
616 /****************************************************************************
617 Change a unix mode to a dos mode.
618 May also read the create timespec into the stat struct in smb_fname
619 if "store dos attributes" is true.
620 ****************************************************************************/
622 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
624 uint32 result = 0;
625 bool offline, used_stat_dos_flags = false;
627 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
629 if (!VALID_STAT(smb_fname->st)) {
630 return 0;
633 /* First do any modifications that depend on the path name. */
634 /* hide files with a name starting with a . */
635 if (lp_hide_dot_files(SNUM(conn))) {
636 const char *p = strrchr_m(smb_fname->base_name,'/');
637 if (p) {
638 p++;
639 } else {
640 p = smb_fname->base_name;
643 /* Only . and .. are not hidden. */
644 if (p[0] == '.' && !((p[1] == '\0') ||
645 (p[1] == '.' && p[2] == '\0'))) {
646 result |= FILE_ATTRIBUTE_HIDDEN;
650 #ifdef HAVE_STAT_DOS_FLAGS
651 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
652 #endif
653 if (!used_stat_dos_flags) {
654 /* Get the DOS attributes from an EA by preference. */
655 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
656 result |= dos_mode_from_sbuf(conn, smb_fname);
660 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
661 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
662 result |= FILE_ATTRIBUTE_OFFLINE;
665 /* Optimization : Only call is_hidden_path if it's not already
666 hidden. */
667 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
668 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
669 result |= FILE_ATTRIBUTE_HIDDEN;
672 if (result == 0) {
673 result = FILE_ATTRIBUTE_NORMAL;
676 result = filter_mode_by_protocol(result);
678 DEBUG(8,("dos_mode returning "));
680 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
681 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
682 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
683 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
684 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
685 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
687 DEBUG(8,("\n"));
689 return(result);
692 /*******************************************************************
693 chmod a file - but preserve some bits.
694 If "store dos attributes" is also set it will store the create time
695 from the stat struct in smb_fname (in NTTIME format) in the EA
696 attribute also.
697 ********************************************************************/
699 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
700 uint32 dosmode, const char *parent_dir, bool newfile)
702 int mask=0;
703 mode_t tmp;
704 mode_t unixmode;
705 int ret = -1, lret = -1;
706 uint32_t old_mode;
707 struct timespec new_create_timespec;
709 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
710 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
712 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
713 dosmode, smb_fname_str_dbg(smb_fname)));
715 unixmode = smb_fname->st.st_ex_mode;
717 get_acl_group_bits(conn, smb_fname->base_name,
718 &smb_fname->st.st_ex_mode);
720 if (S_ISDIR(smb_fname->st.st_ex_mode))
721 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
722 else
723 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
725 new_create_timespec = smb_fname->st.st_ex_btime;
727 old_mode = dos_mode(conn, smb_fname);
729 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
730 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
731 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
732 if (lret == -1) {
733 DEBUG(0, ("set_dos_mode: client has asked to "
734 "set FILE_ATTRIBUTE_OFFLINE to "
735 "%s/%s but there was an error while "
736 "setting it or it is not "
737 "supported.\n", parent_dir,
738 smb_fname_str_dbg(smb_fname)));
743 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
744 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
746 smb_fname->st.st_ex_btime = new_create_timespec;
748 #ifdef HAVE_STAT_DOS_FLAGS
750 bool attributes_changed;
752 if (set_stat_dos_flags(conn, smb_fname, dosmode,
753 &attributes_changed))
755 if (!newfile && attributes_changed) {
756 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
757 FILE_NOTIFY_CHANGE_ATTRIBUTES,
758 smb_fname->base_name);
760 smb_fname->st.st_ex_mode = unixmode;
761 return 0;
764 #endif
765 /* Store the DOS attributes in an EA by preference. */
766 if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
767 if (!newfile) {
768 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
769 FILE_NOTIFY_CHANGE_ATTRIBUTES,
770 smb_fname->base_name);
772 smb_fname->st.st_ex_mode = unixmode;
773 return 0;
776 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
778 /* preserve the s bits */
779 mask |= (S_ISUID | S_ISGID);
781 /* preserve the t bit */
782 #ifdef S_ISVTX
783 mask |= S_ISVTX;
784 #endif
786 /* possibly preserve the x bits */
787 if (!MAP_ARCHIVE(conn))
788 mask |= S_IXUSR;
789 if (!MAP_SYSTEM(conn))
790 mask |= S_IXGRP;
791 if (!MAP_HIDDEN(conn))
792 mask |= S_IXOTH;
794 unixmode |= (smb_fname->st.st_ex_mode & mask);
796 /* if we previously had any r bits set then leave them alone */
797 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
798 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
799 unixmode |= tmp;
802 /* if we previously had any w bits set then leave them alone
803 whilst adding in the new w bits, if the new mode is not rdonly */
804 if (!IS_DOS_READONLY(dosmode)) {
805 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
809 * From the chmod 2 man page:
811 * "If the calling process is not privileged, and the group of the file
812 * does not match the effective group ID of the process or one of its
813 * supplementary group IDs, the S_ISGID bit will be turned off, but
814 * this will not cause an error to be returned."
816 * Simply refuse to do the chmod in this case.
819 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
820 geteuid() != sec_initial_uid() &&
821 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
822 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
823 "set for directory %s\n",
824 smb_fname_str_dbg(smb_fname)));
825 errno = EPERM;
826 return -1;
829 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
830 if (ret == 0) {
831 if(!newfile || (lret != -1)) {
832 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
833 FILE_NOTIFY_CHANGE_ATTRIBUTES,
834 smb_fname->base_name);
836 smb_fname->st.st_ex_mode = unixmode;
837 return 0;
840 if((errno != EPERM) && (errno != EACCES))
841 return -1;
843 if(!lp_dos_filemode(SNUM(conn)))
844 return -1;
846 /* We want DOS semantics, ie allow non owner with write permission to change the
847 bits on a file. Just like file_ntimes below.
850 /* Check if we have write access. */
851 if (CAN_WRITE(conn)) {
853 * We need to open the file with write access whilst
854 * still in our current user context. This ensures we
855 * are not violating security in doing the fchmod.
857 files_struct *fsp;
858 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
859 &fsp)))
860 return -1;
861 become_root();
862 ret = SMB_VFS_FCHMOD(fsp, unixmode);
863 unbecome_root();
864 close_file(NULL, fsp, NORMAL_CLOSE);
865 if (!newfile) {
866 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
867 FILE_NOTIFY_CHANGE_ATTRIBUTES,
868 smb_fname->base_name);
870 if (ret == 0) {
871 smb_fname->st.st_ex_mode = unixmode;
875 return( ret );
879 NTSTATUS file_set_sparse(connection_struct *conn,
880 files_struct *fsp,
881 bool sparse)
883 uint32_t old_dosmode;
884 uint32_t new_dosmode;
885 NTSTATUS status;
887 if (!CAN_WRITE(conn)) {
888 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
889 "on readonly share[%s]\n",
890 smb_fname_str_dbg(fsp->fsp_name),
891 sparse,
892 lp_servicename(SNUM(conn))));
893 return NT_STATUS_MEDIA_WRITE_PROTECTED;
896 if (!(fsp->access_mask & FILE_WRITE_DATA) &&
897 !(fsp->access_mask & FILE_WRITE_ATTRIBUTES)) {
898 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
899 "access_mask[0x%08X] - access denied\n",
900 smb_fname_str_dbg(fsp->fsp_name),
901 sparse,
902 fsp->access_mask));
903 return NT_STATUS_ACCESS_DENIED;
906 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
907 sparse, smb_fname_str_dbg(fsp->fsp_name)));
909 if (!lp_store_dos_attributes(SNUM(conn))) {
910 return NT_STATUS_INVALID_DEVICE_REQUEST;
913 status = vfs_stat_fsp(fsp);
914 if (!NT_STATUS_IS_OK(status)) {
915 return status;
918 old_dosmode = dos_mode(conn, fsp->fsp_name);
920 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
921 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
922 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
923 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
924 } else {
925 return NT_STATUS_OK;
928 /* Store the DOS attributes in an EA. */
929 if (!set_ea_dos_attribute(conn, fsp->fsp_name,
930 new_dosmode)) {
931 if (errno == 0) {
932 errno = EIO;
934 return map_nt_error_from_unix(errno);
937 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
938 FILE_NOTIFY_CHANGE_ATTRIBUTES,
939 fsp->fsp_name->base_name);
941 fsp->is_sparse = sparse;
943 return NT_STATUS_OK;
946 /*******************************************************************
947 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
948 than POSIX.
949 *******************************************************************/
951 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
952 struct smb_file_time *ft)
954 int ret = -1;
956 errno = 0;
958 DEBUG(6, ("file_ntime: actime: %s",
959 time_to_asc(convert_timespec_to_time_t(ft->atime))));
960 DEBUG(6, ("file_ntime: modtime: %s",
961 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
962 DEBUG(6, ("file_ntime: ctime: %s",
963 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
964 DEBUG(6, ("file_ntime: createtime: %s",
965 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
967 /* Don't update the time on read-only shares */
968 /* We need this as set_filetime (which can be called on
969 close and other paths) can end up calling this function
970 without the NEED_WRITE protection. Found by :
971 Leo Weppelman <leo@wau.mis.ah.nl>
974 if (!CAN_WRITE(conn)) {
975 return 0;
978 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
979 return 0;
982 if((errno != EPERM) && (errno != EACCES)) {
983 return -1;
986 if(!lp_dos_filetimes(SNUM(conn))) {
987 return -1;
990 /* We have permission (given by the Samba admin) to
991 break POSIX semantics and allow a user to change
992 the time on a file they don't own but can write to
993 (as DOS does).
996 /* Check if we have write access. */
997 if (can_write_to_file(conn, smb_fname)) {
998 /* We are allowed to become root and change the filetime. */
999 become_root();
1000 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1001 unbecome_root();
1004 return ret;
1007 /******************************************************************
1008 Force a "sticky" write time on a pathname. This will always be
1009 returned on all future write time queries and set on close.
1010 ******************************************************************/
1012 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1014 if (null_timespec(mtime)) {
1015 return true;
1018 if (!set_sticky_write_time(fileid, mtime)) {
1019 return false;
1022 return true;
1025 /******************************************************************
1026 Force a "sticky" write time on an fsp. This will always be
1027 returned on all future write time queries and set on close.
1028 ******************************************************************/
1030 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1032 if (null_timespec(mtime)) {
1033 return true;
1036 fsp->write_time_forced = true;
1037 TALLOC_FREE(fsp->update_write_time_event);
1039 return set_sticky_write_time_path(fsp->file_id, mtime);
1042 /******************************************************************
1043 Set a create time EA.
1044 ******************************************************************/
1046 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1047 const struct smb_filename *psmb_fname,
1048 struct timespec create_time)
1050 NTSTATUS status;
1051 struct smb_filename *smb_fname = NULL;
1052 uint32_t dosmode;
1053 int ret;
1055 if (!lp_store_dos_attributes(SNUM(conn))) {
1056 return NT_STATUS_OK;
1059 status = create_synthetic_smb_fname(talloc_tos(),
1060 psmb_fname->base_name,
1061 NULL, &psmb_fname->st,
1062 &smb_fname);
1064 if (!NT_STATUS_IS_OK(status)) {
1065 return status;
1068 dosmode = dos_mode(conn, smb_fname);
1070 smb_fname->st.st_ex_btime = create_time;
1072 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1073 if (ret == -1) {
1074 map_nt_error_from_unix(errno);
1077 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1078 smb_fname_str_dbg(smb_fname)));
1080 return NT_STATUS_OK;
1083 /******************************************************************
1084 Return a create time.
1085 ******************************************************************/
1087 struct timespec get_create_timespec(connection_struct *conn,
1088 struct files_struct *fsp,
1089 const struct smb_filename *smb_fname)
1091 return smb_fname->st.st_ex_btime;
1094 /******************************************************************
1095 Return a change time (may look at EA in future).
1096 ******************************************************************/
1098 struct timespec get_change_timespec(connection_struct *conn,
1099 struct files_struct *fsp,
1100 const struct smb_filename *smb_fname)
1102 return smb_fname->st.st_ex_mtime;