Fix bug #7996 - sgid bit lost on folder rename.
[Samba/gbeck.git] / source3 / smbd / dosmode.c
blob1ea4c686d51a02469e0d88d8cb83b8bd40a8edb3
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 aRONLY;
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 (ro_opts == MAP_READONLY_YES) {
174 /* Original Samba method - map inverse of user "w" bit. */
175 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
176 result |= aRONLY;
178 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
179 /* Check actual permissions for read-only. */
180 if (!can_write_to_file(conn, smb_fname)) {
181 result |= aRONLY;
183 } /* Else never set the readonly bit. */
185 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
186 result |= aARCH;
188 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
189 result |= aSYSTEM;
191 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
192 result |= aHIDDEN;
194 if (S_ISDIR(smb_fname->st.st_ex_mode))
195 result = aDIR | (result & aRONLY);
197 result |= set_link_read_only_flag(&smb_fname->st);
199 DEBUG(8,("dos_mode_from_sbuf returning "));
201 if (result & aHIDDEN) DEBUG(8, ("h"));
202 if (result & aRONLY ) DEBUG(8, ("r"));
203 if (result & aSYSTEM) DEBUG(8, ("s"));
204 if (result & aDIR ) DEBUG(8, ("d"));
205 if (result & aARCH ) DEBUG(8, ("a"));
207 DEBUG(8,("\n"));
208 return result;
211 /****************************************************************************
212 Get DOS attributes from an EA.
213 This can also pull the create time into the stat struct inside smb_fname.
214 ****************************************************************************/
216 static bool get_ea_dos_attribute(connection_struct *conn,
217 struct smb_filename *smb_fname,
218 uint32 *pattr)
220 struct xattr_DOSATTRIB dosattrib;
221 enum ndr_err_code ndr_err;
222 DATA_BLOB blob;
223 ssize_t sizeret;
224 fstring attrstr;
225 uint32_t dosattr;
227 if (!lp_store_dos_attributes(SNUM(conn))) {
228 return False;
231 /* Don't reset pattr to zero as we may already have filename-based attributes we
232 need to preserve. */
234 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
235 SAMBA_XATTR_DOS_ATTRIB, attrstr,
236 sizeof(attrstr));
237 if (sizeret == -1) {
238 if (errno == ENOSYS
239 #if defined(ENOTSUP)
240 || errno == ENOTSUP) {
241 #else
243 #endif
244 DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
245 "from EA on file %s: Error = %s\n",
246 smb_fname_str_dbg(smb_fname),
247 strerror(errno)));
248 set_store_dos_attributes(SNUM(conn), False);
250 return False;
253 blob.data = (uint8_t *)attrstr;
254 blob.length = sizeret;
256 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
257 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
259 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
260 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
261 "from EA on file %s: Error = %s\n",
262 smb_fname_str_dbg(smb_fname),
263 ndr_errstr(ndr_err)));
264 return false;
267 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
268 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
270 switch (dosattrib.version) {
271 case 0xFFFF:
272 dosattr = dosattrib.info.compatinfoFFFF.attrib;
273 break;
274 case 1:
275 dosattr = dosattrib.info.info1.attrib;
276 if (!null_nttime(dosattrib.info.info1.create_time)) {
277 struct timespec create_time =
278 nt_time_to_unix_timespec(
279 &dosattrib.info.info1.create_time);
281 update_stat_ex_create_time(&smb_fname->st,
282 create_time);
284 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
285 "set btime %s\n",
286 smb_fname_str_dbg(smb_fname),
287 time_to_asc(convert_timespec_to_time_t(
288 create_time)) ));
290 break;
291 case 2:
292 dosattr = dosattrib.info.oldinfo2.attrib;
293 /* Don't know what flags to check for this case. */
294 break;
295 case 3:
296 dosattr = dosattrib.info.info3.attrib;
297 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
298 !null_nttime(dosattrib.info.info3.create_time)) {
299 struct timespec create_time =
300 nt_time_to_unix_timespec(
301 &dosattrib.info.info3.create_time);
303 update_stat_ex_create_time(&smb_fname->st,
304 create_time);
306 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
307 "set btime %s\n",
308 smb_fname_str_dbg(smb_fname),
309 time_to_asc(convert_timespec_to_time_t(
310 create_time)) ));
312 break;
313 default:
314 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
315 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
316 attrstr));
317 return false;
320 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
321 dosattr |= aDIR;
323 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
324 *pattr = (uint32)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
326 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
328 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
329 if (dosattr & aRONLY ) DEBUG(8, ("r"));
330 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
331 if (dosattr & aDIR ) DEBUG(8, ("d"));
332 if (dosattr & aARCH ) DEBUG(8, ("a"));
334 DEBUG(8,("\n"));
336 return True;
339 /****************************************************************************
340 Set DOS attributes in an EA.
341 Also sets the create time.
342 ****************************************************************************/
344 static bool set_ea_dos_attribute(connection_struct *conn,
345 struct smb_filename *smb_fname,
346 uint32 dosmode)
348 struct xattr_DOSATTRIB dosattrib;
349 enum ndr_err_code ndr_err;
350 DATA_BLOB blob;
351 files_struct *fsp = NULL;
352 bool ret = false;
354 if (!lp_store_dos_attributes(SNUM(conn))) {
355 return False;
358 ZERO_STRUCT(dosattrib);
359 ZERO_STRUCT(blob);
361 dosattrib.version = 3;
362 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
363 XATTR_DOSINFO_CREATE_TIME;
364 dosattrib.info.info3.attrib = dosmode;
365 unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
366 smb_fname->st.st_ex_btime);
368 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
369 (unsigned int)dosmode,
370 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
371 smb_fname_str_dbg(smb_fname) ));
373 ndr_err = ndr_push_struct_blob(
374 &blob, talloc_tos(), &dosattrib,
375 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
377 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
378 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
379 ndr_errstr(ndr_err)));
380 return false;
383 if (blob.data == NULL || blob.length == 0) {
384 return false;
387 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
388 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
389 0) == -1) {
390 if((errno != EPERM) && (errno != EACCES)) {
391 if (errno == ENOSYS
392 #if defined(ENOTSUP)
393 || errno == ENOTSUP) {
394 #else
396 #endif
397 DEBUG(1,("set_ea_dos_attributes: Cannot set "
398 "attribute EA on file %s: Error = %s\n",
399 smb_fname_str_dbg(smb_fname),
400 strerror(errno) ));
401 set_store_dos_attributes(SNUM(conn), False);
403 return false;
406 /* We want DOS semantics, ie allow non owner with write permission to change the
407 bits on a file. Just like file_ntimes below.
410 /* Check if we have write access. */
411 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
412 return false;
415 * We need to open the file with write access whilst
416 * still in our current user context. This ensures we
417 * are not violating security in doing the setxattr.
420 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
421 &fsp)))
422 return ret;
423 become_root();
424 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
425 SAMBA_XATTR_DOS_ATTRIB, blob.data,
426 blob.length, 0) == 0) {
427 ret = true;
429 unbecome_root();
430 close_file(NULL, fsp, NORMAL_CLOSE);
431 return ret;
433 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
434 (unsigned int)dosmode,
435 smb_fname_str_dbg(smb_fname)));
436 return true;
439 /****************************************************************************
440 Change a unix mode to a dos mode for an ms dfs link.
441 ****************************************************************************/
443 uint32 dos_mode_msdfs(connection_struct *conn,
444 const struct smb_filename *smb_fname)
446 uint32 result = 0;
448 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
450 if (!VALID_STAT(smb_fname->st)) {
451 return 0;
454 /* First do any modifications that depend on the path name. */
455 /* hide files with a name starting with a . */
456 if (lp_hide_dot_files(SNUM(conn))) {
457 const char *p = strrchr_m(smb_fname->base_name, '/');
458 if (p) {
459 p++;
460 } else {
461 p = smb_fname->base_name;
464 /* Only . and .. are not hidden. */
465 if (p[0] == '.' && !((p[1] == '\0') ||
466 (p[1] == '.' && p[2] == '\0'))) {
467 result |= aHIDDEN;
471 result |= dos_mode_from_sbuf(conn, smb_fname);
473 /* Optimization : Only call is_hidden_path if it's not already
474 hidden. */
475 if (!(result & aHIDDEN) &&
476 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
477 result |= aHIDDEN;
480 if (result == 0) {
481 result = FILE_ATTRIBUTE_NORMAL;
484 result = filter_mode_by_protocol(result);
486 DEBUG(8,("dos_mode_msdfs returning "));
488 if (result & aHIDDEN) DEBUG(8, ("h"));
489 if (result & aRONLY ) DEBUG(8, ("r"));
490 if (result & aSYSTEM) DEBUG(8, ("s"));
491 if (result & aDIR ) DEBUG(8, ("d"));
492 if (result & aARCH ) DEBUG(8, ("a"));
493 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
495 DEBUG(8,("\n"));
497 return(result);
500 #ifdef HAVE_STAT_DOS_FLAGS
501 /****************************************************************************
502 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
503 ****************************************************************************/
505 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
507 uint32_t dos_stat_flags = 0;
509 if (dosmode & aARCH)
510 dos_stat_flags |= UF_DOS_ARCHIVE;
511 if (dosmode & aHIDDEN)
512 dos_stat_flags |= UF_DOS_HIDDEN;
513 if (dosmode & aRONLY)
514 dos_stat_flags |= UF_DOS_RO;
515 if (dosmode & aSYSTEM)
516 dos_stat_flags |= UF_DOS_SYSTEM;
517 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
518 dos_stat_flags |= UF_DOS_NOINDEX;
520 return dos_stat_flags;
523 /****************************************************************************
524 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
525 ****************************************************************************/
527 static bool get_stat_dos_flags(connection_struct *conn,
528 const struct smb_filename *smb_fname,
529 uint32_t *dosmode)
531 SMB_ASSERT(VALID_STAT(smb_fname->st));
532 SMB_ASSERT(dosmode);
534 if (!lp_store_dos_attributes(SNUM(conn))) {
535 return false;
538 DEBUG(5, ("Getting stat dos attributes for %s.\n",
539 smb_fname_str_dbg(smb_fname)));
541 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
542 *dosmode |= aARCH;
543 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
544 *dosmode |= aHIDDEN;
545 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
546 *dosmode |= aRONLY;
547 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
548 *dosmode |= aSYSTEM;
549 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
550 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
551 if (smb_fname->st.st_ex_flags & FILE_ATTRIBUTE_SPARSE)
552 *dosmode |= FILE_ATTRIBUTE_SPARSE;
553 if (S_ISDIR(smb_fname->st.st_ex_mode))
554 *dosmode |= aDIR;
556 *dosmode |= set_link_read_only_flag(&smb_fname->st);
558 return true;
561 /****************************************************************************
562 Sets DOS attributes, stored in st_ex_flags of the inode.
563 ****************************************************************************/
565 static bool set_stat_dos_flags(connection_struct *conn,
566 const struct smb_filename *smb_fname,
567 uint32_t dosmode,
568 bool *attributes_changed)
570 uint32_t new_flags = 0;
571 int error = 0;
573 SMB_ASSERT(VALID_STAT(smb_fname->st));
574 SMB_ASSERT(attributes_changed);
576 *attributes_changed = false;
578 if (!lp_store_dos_attributes(SNUM(conn))) {
579 return false;
582 DEBUG(5, ("Setting stat dos attributes for %s.\n",
583 smb_fname_str_dbg(smb_fname)));
585 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
586 dos_attributes_to_stat_dos_flags(dosmode);
588 /* Return early if no flags changed. */
589 if (new_flags == smb_fname->st.st_ex_flags)
590 return true;
592 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
593 smb_fname->st.st_ex_flags));
595 /* Set new flags with chflags. */
596 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
597 if (error) {
598 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
599 "file %s! errno=%d\n", new_flags,
600 smb_fname_str_dbg(smb_fname), errno));
601 return false;
604 *attributes_changed = true;
605 return true;
607 #endif /* HAVE_STAT_DOS_FLAGS */
609 /****************************************************************************
610 Change a unix mode to a dos mode.
611 May also read the create timespec into the stat struct in smb_fname
612 if "store dos attributes" is true.
613 ****************************************************************************/
615 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
617 uint32 result = 0;
618 bool offline, used_stat_dos_flags = false;
620 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
622 if (!VALID_STAT(smb_fname->st)) {
623 return 0;
626 /* First do any modifications that depend on the path name. */
627 /* hide files with a name starting with a . */
628 if (lp_hide_dot_files(SNUM(conn))) {
629 const char *p = strrchr_m(smb_fname->base_name,'/');
630 if (p) {
631 p++;
632 } else {
633 p = smb_fname->base_name;
636 /* Only . and .. are not hidden. */
637 if (p[0] == '.' && !((p[1] == '\0') ||
638 (p[1] == '.' && p[2] == '\0'))) {
639 result |= aHIDDEN;
643 #ifdef HAVE_STAT_DOS_FLAGS
644 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
645 #endif
646 if (!used_stat_dos_flags) {
647 /* Get the DOS attributes from an EA by preference. */
648 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
649 result |= dos_mode_from_sbuf(conn, smb_fname);
653 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
654 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
655 result |= FILE_ATTRIBUTE_OFFLINE;
658 /* Optimization : Only call is_hidden_path if it's not already
659 hidden. */
660 if (!(result & aHIDDEN) &&
661 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
662 result |= aHIDDEN;
665 if (result == 0) {
666 result = FILE_ATTRIBUTE_NORMAL;
669 result = filter_mode_by_protocol(result);
671 DEBUG(8,("dos_mode returning "));
673 if (result & aHIDDEN) DEBUG(8, ("h"));
674 if (result & aRONLY ) DEBUG(8, ("r"));
675 if (result & aSYSTEM) DEBUG(8, ("s"));
676 if (result & aDIR ) DEBUG(8, ("d"));
677 if (result & aARCH ) DEBUG(8, ("a"));
678 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
680 DEBUG(8,("\n"));
682 return(result);
685 /*******************************************************************
686 chmod a file - but preserve some bits.
687 If "store dos attributes" is also set it will store the create time
688 from the stat struct in smb_fname (in NTTIME format) in the EA
689 attribute also.
690 ********************************************************************/
692 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
693 uint32 dosmode, const char *parent_dir, bool newfile)
695 int mask=0;
696 mode_t tmp;
697 mode_t unixmode;
698 int ret = -1, lret = -1;
699 uint32_t old_mode;
700 struct timespec new_create_timespec;
702 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
703 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
705 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
706 dosmode, smb_fname_str_dbg(smb_fname)));
708 unixmode = smb_fname->st.st_ex_mode;
710 get_acl_group_bits(conn, smb_fname->base_name,
711 &smb_fname->st.st_ex_mode);
713 if (S_ISDIR(smb_fname->st.st_ex_mode))
714 dosmode |= aDIR;
715 else
716 dosmode &= ~aDIR;
718 new_create_timespec = smb_fname->st.st_ex_btime;
720 old_mode = dos_mode(conn, smb_fname);
722 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
723 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
724 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
725 if (lret == -1) {
726 DEBUG(0, ("set_dos_mode: client has asked to "
727 "set FILE_ATTRIBUTE_OFFLINE to "
728 "%s/%s but there was an error while "
729 "setting it or it is not "
730 "supported.\n", parent_dir,
731 smb_fname_str_dbg(smb_fname)));
736 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
737 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
739 smb_fname->st.st_ex_btime = new_create_timespec;
741 #ifdef HAVE_STAT_DOS_FLAGS
743 bool attributes_changed;
745 if (set_stat_dos_flags(conn, smb_fname, dosmode,
746 &attributes_changed))
748 if (!newfile && attributes_changed) {
749 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
750 FILE_NOTIFY_CHANGE_ATTRIBUTES,
751 smb_fname->base_name);
753 smb_fname->st.st_ex_mode = unixmode;
754 return 0;
757 #endif
758 /* Store the DOS attributes in an EA by preference. */
759 if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
760 if (!newfile) {
761 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
762 FILE_NOTIFY_CHANGE_ATTRIBUTES,
763 smb_fname->base_name);
765 smb_fname->st.st_ex_mode = unixmode;
766 return 0;
769 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
771 /* preserve the s bits */
772 mask |= (S_ISUID | S_ISGID);
774 /* preserve the t bit */
775 #ifdef S_ISVTX
776 mask |= S_ISVTX;
777 #endif
779 /* possibly preserve the x bits */
780 if (!MAP_ARCHIVE(conn))
781 mask |= S_IXUSR;
782 if (!MAP_SYSTEM(conn))
783 mask |= S_IXGRP;
784 if (!MAP_HIDDEN(conn))
785 mask |= S_IXOTH;
787 unixmode |= (smb_fname->st.st_ex_mode & mask);
789 /* if we previously had any r bits set then leave them alone */
790 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
791 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
792 unixmode |= tmp;
795 /* if we previously had any w bits set then leave them alone
796 whilst adding in the new w bits, if the new mode is not rdonly */
797 if (!IS_DOS_READONLY(dosmode)) {
798 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
802 * From the chmod 2 man page:
804 * "If the calling process is not privileged, and the group of the file
805 * does not match the effective group ID of the process or one of its
806 * supplementary group IDs, the S_ISGID bit will be turned off, but
807 * this will not cause an error to be returned."
809 * Simply refuse to do the chmod in this case.
812 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
813 geteuid() != sec_initial_uid() &&
814 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
815 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
816 "set for directory %s\n",
817 smb_fname_str_dbg(smb_fname)));
818 errno = EPERM;
819 return -1;
822 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
823 if (ret == 0) {
824 if(!newfile || (lret != -1)) {
825 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
826 FILE_NOTIFY_CHANGE_ATTRIBUTES,
827 smb_fname->base_name);
829 smb_fname->st.st_ex_mode = unixmode;
830 return 0;
833 if((errno != EPERM) && (errno != EACCES))
834 return -1;
836 if(!lp_dos_filemode(SNUM(conn)))
837 return -1;
839 /* We want DOS semantics, ie allow non owner with write permission to change the
840 bits on a file. Just like file_ntimes below.
843 /* Check if we have write access. */
844 if (CAN_WRITE(conn)) {
846 * We need to open the file with write access whilst
847 * still in our current user context. This ensures we
848 * are not violating security in doing the fchmod.
850 files_struct *fsp;
851 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
852 &fsp)))
853 return -1;
854 become_root();
855 ret = SMB_VFS_FCHMOD(fsp, unixmode);
856 unbecome_root();
857 close_file(NULL, fsp, NORMAL_CLOSE);
858 if (!newfile) {
859 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
860 FILE_NOTIFY_CHANGE_ATTRIBUTES,
861 smb_fname->base_name);
863 if (ret == 0) {
864 smb_fname->st.st_ex_mode = unixmode;
868 return( ret );
872 NTSTATUS file_set_sparse(connection_struct *conn,
873 files_struct *fsp,
874 bool sparse)
876 uint32_t old_dosmode;
877 uint32_t new_dosmode;
878 NTSTATUS status;
880 if (!CAN_WRITE(conn)) {
881 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
882 "on readonly share[%s]\n",
883 smb_fname_str_dbg(fsp->fsp_name),
884 sparse,
885 lp_servicename(SNUM(conn))));
886 return NT_STATUS_MEDIA_WRITE_PROTECTED;
889 if (!(fsp->access_mask & FILE_WRITE_DATA) &&
890 !(fsp->access_mask & FILE_WRITE_ATTRIBUTES)) {
891 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
892 "access_mask[0x%08X] - access denied\n",
893 smb_fname_str_dbg(fsp->fsp_name),
894 sparse,
895 fsp->access_mask));
896 return NT_STATUS_ACCESS_DENIED;
899 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
900 sparse, smb_fname_str_dbg(fsp->fsp_name)));
902 if (!lp_store_dos_attributes(SNUM(conn))) {
903 return NT_STATUS_INVALID_DEVICE_REQUEST;
906 status = vfs_stat_fsp(fsp);
907 if (!NT_STATUS_IS_OK(status)) {
908 return status;
911 old_dosmode = dos_mode(conn, fsp->fsp_name);
913 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
914 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
915 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
916 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
917 } else {
918 return NT_STATUS_OK;
921 /* Store the DOS attributes in an EA. */
922 if (!set_ea_dos_attribute(conn, fsp->fsp_name,
923 new_dosmode)) {
924 if (errno == 0) {
925 errno = EIO;
927 return map_nt_error_from_unix(errno);
930 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
931 FILE_NOTIFY_CHANGE_ATTRIBUTES,
932 fsp->fsp_name->base_name);
934 fsp->is_sparse = sparse;
936 return NT_STATUS_OK;
939 /*******************************************************************
940 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
941 than POSIX.
942 *******************************************************************/
944 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
945 struct smb_file_time *ft)
947 int ret = -1;
949 errno = 0;
951 DEBUG(6, ("file_ntime: actime: %s",
952 time_to_asc(convert_timespec_to_time_t(ft->atime))));
953 DEBUG(6, ("file_ntime: modtime: %s",
954 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
955 DEBUG(6, ("file_ntime: ctime: %s",
956 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
957 DEBUG(6, ("file_ntime: createtime: %s",
958 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
960 /* Don't update the time on read-only shares */
961 /* We need this as set_filetime (which can be called on
962 close and other paths) can end up calling this function
963 without the NEED_WRITE protection. Found by :
964 Leo Weppelman <leo@wau.mis.ah.nl>
967 if (!CAN_WRITE(conn)) {
968 return 0;
971 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
972 return 0;
975 if((errno != EPERM) && (errno != EACCES)) {
976 return -1;
979 if(!lp_dos_filetimes(SNUM(conn))) {
980 return -1;
983 /* We have permission (given by the Samba admin) to
984 break POSIX semantics and allow a user to change
985 the time on a file they don't own but can write to
986 (as DOS does).
989 /* Check if we have write access. */
990 if (can_write_to_file(conn, smb_fname)) {
991 /* We are allowed to become root and change the filetime. */
992 become_root();
993 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
994 unbecome_root();
997 return ret;
1000 /******************************************************************
1001 Force a "sticky" write time on a pathname. This will always be
1002 returned on all future write time queries and set on close.
1003 ******************************************************************/
1005 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1007 if (null_timespec(mtime)) {
1008 return true;
1011 if (!set_sticky_write_time(fileid, mtime)) {
1012 return false;
1015 return true;
1018 /******************************************************************
1019 Force a "sticky" write time on an fsp. This will always be
1020 returned on all future write time queries and set on close.
1021 ******************************************************************/
1023 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1025 if (null_timespec(mtime)) {
1026 return true;
1029 fsp->write_time_forced = true;
1030 TALLOC_FREE(fsp->update_write_time_event);
1032 return set_sticky_write_time_path(fsp->file_id, mtime);
1035 /******************************************************************
1036 Set a create time EA.
1037 ******************************************************************/
1039 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1040 const struct smb_filename *psmb_fname,
1041 struct timespec create_time)
1043 NTSTATUS status;
1044 struct smb_filename *smb_fname = NULL;
1045 uint32_t dosmode;
1046 int ret;
1048 if (!lp_store_dos_attributes(SNUM(conn))) {
1049 return NT_STATUS_OK;
1052 status = create_synthetic_smb_fname(talloc_tos(),
1053 psmb_fname->base_name,
1054 NULL, &psmb_fname->st,
1055 &smb_fname);
1057 if (!NT_STATUS_IS_OK(status)) {
1058 return status;
1061 dosmode = dos_mode(conn, smb_fname);
1063 smb_fname->st.st_ex_btime = create_time;
1065 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1066 if (ret == -1) {
1067 map_nt_error_from_unix(errno);
1070 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1071 smb_fname_str_dbg(smb_fname)));
1073 return NT_STATUS_OK;
1076 /******************************************************************
1077 Return a create time.
1078 ******************************************************************/
1080 struct timespec get_create_timespec(connection_struct *conn,
1081 struct files_struct *fsp,
1082 const struct smb_filename *smb_fname)
1084 return smb_fname->st.st_ex_btime;
1087 /******************************************************************
1088 Return a change time (may look at EA in future).
1089 ******************************************************************/
1091 struct timespec get_change_timespec(connection_struct *conn,
1092 struct files_struct *fsp,
1093 const struct smb_filename *smb_fname)
1095 return smb_fname->st.st_ex_mtime;