s3: smbd: change file_set_dosmode() to use get_file_handle_for_metadata() instead...
[Samba.git] / source3 / smbd / dosmode.c
blobb99e18033cc9e6ec339007623d8dbb8fb0268676
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 NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
29 struct smb_filename *smb_fname,
30 files_struct **ret_fsp,
31 bool *need_close);
33 static uint32_t filter_mode_by_protocol(uint32_t mode)
35 if (get_Protocol() <= PROTOCOL_LANMAN2) {
36 DEBUG(10,("filter_mode_by_protocol: "
37 "filtering result 0x%x to 0x%x\n",
38 (unsigned int)mode,
39 (unsigned int)(mode & 0x3f) ));
40 mode &= 0x3f;
42 return mode;
45 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
47 #ifdef S_ISLNK
48 #if LINKS_READ_ONLY
49 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
50 return FILE_ATTRIBUTE_READONLY;
51 #endif
52 #endif
53 return 0;
56 /****************************************************************************
57 Change a dos mode to a unix mode.
58 Base permission for files:
59 if creating file and inheriting (i.e. parent_dir != NULL)
60 apply read/write bits from parent directory.
61 else
62 everybody gets read bit set
63 dos readonly is represented in unix by removing everyone's write bit
64 dos archive is represented in unix by the user's execute bit
65 dos system is represented in unix by the group's execute bit
66 dos hidden is represented in unix by the other's execute bit
67 if !inheriting {
68 Then apply create mask,
69 then add force bits.
71 Base permission for directories:
72 dos directory is represented in unix by unix's dir bit and the exec bit
73 if !inheriting {
74 Then apply create mask,
75 then add force bits.
77 ****************************************************************************/
79 mode_t unix_mode(connection_struct *conn, int dosmode,
80 const struct smb_filename *smb_fname,
81 const char *inherit_from_dir)
83 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
84 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
85 * inheriting. */
87 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
88 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
91 if ((inherit_from_dir != NULL) && lp_inherit_perms(SNUM(conn))) {
92 struct smb_filename *smb_fname_parent;
94 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
95 smb_fname_str_dbg(smb_fname),
96 inherit_from_dir));
98 smb_fname_parent = synthetic_smb_fname(
99 talloc_tos(), inherit_from_dir, NULL, NULL);
100 if (smb_fname_parent == NULL) {
101 DEBUG(1,("unix_mode(%s) failed, [dir %s]: No memory\n",
102 smb_fname_str_dbg(smb_fname),
103 inherit_from_dir));
104 return(0);
107 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
108 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
109 smb_fname_str_dbg(smb_fname),
110 inherit_from_dir, strerror(errno)));
111 TALLOC_FREE(smb_fname_parent);
112 return(0); /* *** shouldn't happen! *** */
115 /* Save for later - but explicitly remove setuid bit for safety. */
116 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
117 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
118 smb_fname_str_dbg(smb_fname), (int)dir_mode));
119 /* Clear "result" */
120 result = 0;
121 TALLOC_FREE(smb_fname_parent);
124 if (IS_DOS_DIR(dosmode)) {
125 /* We never make directories read only for the owner as under DOS a user
126 can always create a file in a read-only directory. */
127 result |= (S_IFDIR | S_IWUSR);
129 if (dir_mode) {
130 /* Inherit mode of parent directory. */
131 result |= dir_mode;
132 } else {
133 /* Provisionally add all 'x' bits */
134 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
136 /* Apply directory mask */
137 result &= lp_dir_mask(SNUM(conn));
138 /* Add in force bits */
139 result |= lp_force_dir_mode(SNUM(conn));
141 } else {
142 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
143 result |= S_IXUSR;
145 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
146 result |= S_IXGRP;
148 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
149 result |= S_IXOTH;
151 if (dir_mode) {
152 /* Inherit 666 component of parent directory mode */
153 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
154 } else {
155 /* Apply mode mask */
156 result &= lp_create_mask(SNUM(conn));
157 /* Add in force bits */
158 result |= lp_force_create_mode(SNUM(conn));
162 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
163 (int)result));
164 return(result);
167 /****************************************************************************
168 Change a unix mode to a dos mode.
169 ****************************************************************************/
171 static uint32 dos_mode_from_sbuf(connection_struct *conn,
172 const struct smb_filename *smb_fname)
174 int result = 0;
175 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
177 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
178 /* if we can find out if a file is immutable we should report it r/o */
179 if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
180 result |= FILE_ATTRIBUTE_READONLY;
182 #endif
183 if (ro_opts == MAP_READONLY_YES) {
184 /* Original Samba method - map inverse of user "w" bit. */
185 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
186 result |= FILE_ATTRIBUTE_READONLY;
188 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
189 /* Check actual permissions for read-only. */
190 if (!can_write_to_file(conn, smb_fname)) {
191 result |= FILE_ATTRIBUTE_READONLY;
193 } /* Else never set the readonly bit. */
195 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
196 result |= FILE_ATTRIBUTE_ARCHIVE;
198 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
199 result |= FILE_ATTRIBUTE_SYSTEM;
201 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
202 result |= FILE_ATTRIBUTE_HIDDEN;
204 if (S_ISDIR(smb_fname->st.st_ex_mode))
205 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
207 result |= set_link_read_only_flag(&smb_fname->st);
209 DEBUG(8,("dos_mode_from_sbuf returning "));
211 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
212 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
213 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
214 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
215 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
217 DEBUG(8,("\n"));
218 return result;
221 /****************************************************************************
222 Get DOS attributes from an EA.
223 This can also pull the create time into the stat struct inside smb_fname.
224 ****************************************************************************/
226 static bool get_ea_dos_attribute(connection_struct *conn,
227 struct smb_filename *smb_fname,
228 uint32 *pattr)
230 struct xattr_DOSATTRIB dosattrib;
231 enum ndr_err_code ndr_err;
232 DATA_BLOB blob;
233 ssize_t sizeret;
234 fstring attrstr;
235 uint32_t dosattr;
237 if (!lp_store_dos_attributes(SNUM(conn))) {
238 return False;
241 /* Don't reset pattr to zero as we may already have filename-based attributes we
242 need to preserve. */
244 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
245 SAMBA_XATTR_DOS_ATTRIB, attrstr,
246 sizeof(attrstr));
247 if (sizeret == -1) {
248 if (errno == ENOSYS
249 #if defined(ENOTSUP)
250 || errno == ENOTSUP) {
251 #else
253 #endif
254 DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
255 "from EA on file %s: Error = %s\n",
256 smb_fname_str_dbg(smb_fname),
257 strerror(errno)));
258 set_store_dos_attributes(SNUM(conn), False);
260 return False;
263 blob.data = (uint8_t *)attrstr;
264 blob.length = sizeret;
266 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
267 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
269 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
270 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
271 "from EA on file %s: Error = %s\n",
272 smb_fname_str_dbg(smb_fname),
273 ndr_errstr(ndr_err)));
274 return false;
277 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
278 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
280 switch (dosattrib.version) {
281 case 0xFFFF:
282 dosattr = dosattrib.info.compatinfoFFFF.attrib;
283 break;
284 case 1:
285 dosattr = dosattrib.info.info1.attrib;
286 if (!null_nttime(dosattrib.info.info1.create_time)) {
287 struct timespec create_time =
288 nt_time_to_unix_timespec(
289 &dosattrib.info.info1.create_time);
291 update_stat_ex_create_time(&smb_fname->st,
292 create_time);
294 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
295 "set btime %s\n",
296 smb_fname_str_dbg(smb_fname),
297 time_to_asc(convert_timespec_to_time_t(
298 create_time)) ));
300 break;
301 case 2:
302 dosattr = dosattrib.info.oldinfo2.attrib;
303 /* Don't know what flags to check for this case. */
304 break;
305 case 3:
306 dosattr = dosattrib.info.info3.attrib;
307 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
308 !null_nttime(dosattrib.info.info3.create_time)) {
309 struct timespec create_time =
310 nt_time_to_unix_timespec(
311 &dosattrib.info.info3.create_time);
313 update_stat_ex_create_time(&smb_fname->st,
314 create_time);
316 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
317 "set btime %s\n",
318 smb_fname_str_dbg(smb_fname),
319 time_to_asc(convert_timespec_to_time_t(
320 create_time)) ));
322 break;
323 default:
324 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
325 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
326 attrstr));
327 return false;
330 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
331 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
333 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
334 *pattr = (uint32)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
336 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
338 if (dosattr & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
339 if (dosattr & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
340 if (dosattr & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
341 if (dosattr & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
342 if (dosattr & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
344 DEBUG(8,("\n"));
346 return True;
349 /****************************************************************************
350 Set DOS attributes in an EA.
351 Also sets the create time.
352 ****************************************************************************/
354 static bool set_ea_dos_attribute(connection_struct *conn,
355 struct smb_filename *smb_fname,
356 uint32 dosmode)
358 struct xattr_DOSATTRIB dosattrib;
359 enum ndr_err_code ndr_err;
360 DATA_BLOB blob;
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 bool need_close = false;
396 files_struct *fsp = NULL;
398 if((errno != EPERM) && (errno != EACCES)) {
399 if (errno == ENOSYS
400 #if defined(ENOTSUP)
401 || errno == ENOTSUP) {
402 #else
404 #endif
405 DEBUG(1,("set_ea_dos_attributes: Cannot set "
406 "attribute EA on file %s: Error = %s\n",
407 smb_fname_str_dbg(smb_fname),
408 strerror(errno) ));
409 set_store_dos_attributes(SNUM(conn), False);
411 return false;
414 /* We want DOS semantics, ie allow non owner with write permission to change the
415 bits on a file. Just like file_ntimes below.
418 /* Check if we have write access. */
419 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
420 return false;
422 if (!can_write_to_file(conn, smb_fname)) {
423 return false;
427 * We need to get an open file handle to do the
428 * metadata operation under root.
431 if (!NT_STATUS_IS_OK(get_file_handle_for_metadata(conn,
432 smb_fname,
433 &fsp,
434 &need_close))) {
435 return false;
438 become_root();
439 if (SMB_VFS_FSETXATTR(fsp,
440 SAMBA_XATTR_DOS_ATTRIB, blob.data,
441 blob.length, 0) == 0) {
442 ret = true;
444 unbecome_root();
445 if (need_close) {
446 close_file(NULL, fsp, NORMAL_CLOSE);
448 return ret;
450 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
451 (unsigned int)dosmode,
452 smb_fname_str_dbg(smb_fname)));
453 return true;
456 /****************************************************************************
457 Change a unix mode to a dos mode for an ms dfs link.
458 ****************************************************************************/
460 uint32 dos_mode_msdfs(connection_struct *conn,
461 const struct smb_filename *smb_fname)
463 uint32 result = 0;
465 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
467 if (!VALID_STAT(smb_fname->st)) {
468 return 0;
471 /* First do any modifications that depend on the path name. */
472 /* hide files with a name starting with a . */
473 if (lp_hide_dot_files(SNUM(conn))) {
474 const char *p = strrchr_m(smb_fname->base_name, '/');
475 if (p) {
476 p++;
477 } else {
478 p = smb_fname->base_name;
481 /* Only . and .. are not hidden. */
482 if (p[0] == '.' && !((p[1] == '\0') ||
483 (p[1] == '.' && p[2] == '\0'))) {
484 result |= FILE_ATTRIBUTE_HIDDEN;
488 result |= dos_mode_from_sbuf(conn, smb_fname);
490 /* Optimization : Only call is_hidden_path if it's not already
491 hidden. */
492 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
493 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
494 result |= FILE_ATTRIBUTE_HIDDEN;
497 if (result == 0) {
498 result = FILE_ATTRIBUTE_NORMAL;
501 result = filter_mode_by_protocol(result);
504 * Add in that it is a reparse point
506 result |= FILE_ATTRIBUTE_REPARSE_POINT;
508 DEBUG(8,("dos_mode_msdfs returning "));
510 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
511 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
512 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
513 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
514 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
515 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
517 DEBUG(8,("\n"));
519 return(result);
522 #ifdef HAVE_STAT_DOS_FLAGS
523 /****************************************************************************
524 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
525 ****************************************************************************/
527 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
529 uint32_t dos_stat_flags = 0;
531 if (dosmode & FILE_ATTRIBUTE_ARCHIVE)
532 dos_stat_flags |= UF_DOS_ARCHIVE;
533 if (dosmode & FILE_ATTRIBUTE_HIDDEN)
534 dos_stat_flags |= UF_DOS_HIDDEN;
535 if (dosmode & FILE_ATTRIBUTE_READONLY)
536 dos_stat_flags |= UF_DOS_RO;
537 if (dosmode & FILE_ATTRIBUTE_SYSTEM)
538 dos_stat_flags |= UF_DOS_SYSTEM;
539 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
540 dos_stat_flags |= UF_DOS_NOINDEX;
542 return dos_stat_flags;
545 /****************************************************************************
546 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
547 ****************************************************************************/
549 static bool get_stat_dos_flags(connection_struct *conn,
550 const struct smb_filename *smb_fname,
551 uint32_t *dosmode)
553 SMB_ASSERT(VALID_STAT(smb_fname->st));
554 SMB_ASSERT(dosmode);
556 if (!lp_store_dos_attributes(SNUM(conn))) {
557 return false;
560 DEBUG(5, ("Getting stat dos attributes for %s.\n",
561 smb_fname_str_dbg(smb_fname)));
563 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
564 *dosmode |= FILE_ATTRIBUTE_ARCHIVE;
565 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
566 *dosmode |= FILE_ATTRIBUTE_HIDDEN;
567 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
568 *dosmode |= FILE_ATTRIBUTE_READONLY;
569 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
570 *dosmode |= FILE_ATTRIBUTE_SYSTEM;
571 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
572 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
573 if (smb_fname->st.st_ex_flags & FILE_ATTRIBUTE_SPARSE)
574 *dosmode |= FILE_ATTRIBUTE_SPARSE;
575 if (S_ISDIR(smb_fname->st.st_ex_mode))
576 *dosmode |= FILE_ATTRIBUTE_DIRECTORY;
578 *dosmode |= set_link_read_only_flag(&smb_fname->st);
580 return true;
583 /****************************************************************************
584 Sets DOS attributes, stored in st_ex_flags of the inode.
585 ****************************************************************************/
587 static bool set_stat_dos_flags(connection_struct *conn,
588 const struct smb_filename *smb_fname,
589 uint32_t dosmode,
590 bool *attributes_changed)
592 uint32_t new_flags = 0;
593 int error = 0;
595 SMB_ASSERT(VALID_STAT(smb_fname->st));
596 SMB_ASSERT(attributes_changed);
598 *attributes_changed = false;
600 if (!lp_store_dos_attributes(SNUM(conn))) {
601 return false;
604 DEBUG(5, ("Setting stat dos attributes for %s.\n",
605 smb_fname_str_dbg(smb_fname)));
607 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
608 dos_attributes_to_stat_dos_flags(dosmode);
610 /* Return early if no flags changed. */
611 if (new_flags == smb_fname->st.st_ex_flags)
612 return true;
614 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
615 smb_fname->st.st_ex_flags));
617 /* Set new flags with chflags. */
618 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
619 if (error) {
620 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
621 "file %s! errno=%d\n", new_flags,
622 smb_fname_str_dbg(smb_fname), errno));
623 return false;
626 *attributes_changed = true;
627 return true;
629 #endif /* HAVE_STAT_DOS_FLAGS */
631 /****************************************************************************
632 Change a unix mode to a dos mode.
633 May also read the create timespec into the stat struct in smb_fname
634 if "store dos attributes" is true.
635 ****************************************************************************/
637 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
639 uint32 result = 0;
640 bool offline, used_stat_dos_flags = false;
642 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
644 if (!VALID_STAT(smb_fname->st)) {
645 return 0;
648 /* First do any modifications that depend on the path name. */
649 /* hide files with a name starting with a . */
650 if (lp_hide_dot_files(SNUM(conn))) {
651 const char *p = strrchr_m(smb_fname->base_name,'/');
652 if (p) {
653 p++;
654 } else {
655 p = smb_fname->base_name;
658 /* Only . and .. are not hidden. */
659 if (p[0] == '.' && !((p[1] == '\0') ||
660 (p[1] == '.' && p[2] == '\0'))) {
661 result |= FILE_ATTRIBUTE_HIDDEN;
665 #ifdef HAVE_STAT_DOS_FLAGS
666 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
667 #endif
668 if (!used_stat_dos_flags) {
669 /* Get the DOS attributes from an EA by preference. */
670 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
671 result |= dos_mode_from_sbuf(conn, smb_fname);
675 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
676 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
677 result |= FILE_ATTRIBUTE_OFFLINE;
680 /* Optimization : Only call is_hidden_path if it's not already
681 hidden. */
682 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
683 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
684 result |= FILE_ATTRIBUTE_HIDDEN;
687 if (result == 0) {
688 result = FILE_ATTRIBUTE_NORMAL;
691 result = filter_mode_by_protocol(result);
693 DEBUG(8,("dos_mode returning "));
695 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
696 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
697 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
698 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
699 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
700 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
701 if (result & FILE_ATTRIBUTE_OFFLINE ) DEBUG(8, ("[offline]"));
703 DEBUG(8,("\n"));
705 return(result);
708 /*******************************************************************
709 chmod a file - but preserve some bits.
710 If "store dos attributes" is also set it will store the create time
711 from the stat struct in smb_fname (in NTTIME format) in the EA
712 attribute also.
713 ********************************************************************/
715 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
716 uint32 dosmode, const char *parent_dir, bool newfile)
718 int mask=0;
719 mode_t tmp;
720 mode_t unixmode;
721 int ret = -1, lret = -1;
722 uint32_t old_mode;
723 struct timespec new_create_timespec;
724 files_struct *fsp = NULL;
725 bool need_close = false;
726 NTSTATUS status;
728 if (!CAN_WRITE(conn)) {
729 errno = EROFS;
730 return -1;
733 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
734 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
736 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
737 dosmode, smb_fname_str_dbg(smb_fname)));
739 unixmode = smb_fname->st.st_ex_mode;
741 get_acl_group_bits(conn, smb_fname->base_name,
742 &smb_fname->st.st_ex_mode);
744 if (S_ISDIR(smb_fname->st.st_ex_mode))
745 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
746 else
747 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
749 new_create_timespec = smb_fname->st.st_ex_btime;
751 old_mode = dos_mode(conn, smb_fname);
753 if ((dosmode & FILE_ATTRIBUTE_OFFLINE) &&
754 !(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
755 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
756 if (lret == -1) {
757 if (errno == ENOTSUP) {
758 DEBUG(10, ("Setting FILE_ATTRIBUTE_OFFLINE for "
759 "%s/%s is not supported.\n",
760 parent_dir,
761 smb_fname_str_dbg(smb_fname)));
762 } else {
763 DEBUG(0, ("An error occurred while setting "
764 "FILE_ATTRIBUTE_OFFLINE for "
765 "%s/%s: %s", parent_dir,
766 smb_fname_str_dbg(smb_fname),
767 strerror(errno)));
772 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
773 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
775 smb_fname->st.st_ex_btime = new_create_timespec;
777 #ifdef HAVE_STAT_DOS_FLAGS
779 bool attributes_changed;
781 if (set_stat_dos_flags(conn, smb_fname, dosmode,
782 &attributes_changed))
784 if (!newfile && attributes_changed) {
785 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
786 FILE_NOTIFY_CHANGE_ATTRIBUTES,
787 smb_fname->base_name);
789 smb_fname->st.st_ex_mode = unixmode;
790 return 0;
793 #endif
794 /* Store the DOS attributes in an EA by preference. */
795 if (lp_store_dos_attributes(SNUM(conn))) {
797 * Don't fall back to using UNIX modes. Finally
798 * follow the smb.conf manpage.
800 if (!set_ea_dos_attribute(conn, smb_fname, dosmode)) {
801 return -1;
803 if (!newfile) {
804 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
805 FILE_NOTIFY_CHANGE_ATTRIBUTES,
806 smb_fname->base_name);
808 smb_fname->st.st_ex_mode = unixmode;
809 return 0;
812 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
814 /* preserve the file type bits */
815 mask |= S_IFMT;
817 /* preserve the s bits */
818 mask |= (S_ISUID | S_ISGID);
820 /* preserve the t bit */
821 #ifdef S_ISVTX
822 mask |= S_ISVTX;
823 #endif
825 /* possibly preserve the x bits */
826 if (!MAP_ARCHIVE(conn))
827 mask |= S_IXUSR;
828 if (!MAP_SYSTEM(conn))
829 mask |= S_IXGRP;
830 if (!MAP_HIDDEN(conn))
831 mask |= S_IXOTH;
833 unixmode |= (smb_fname->st.st_ex_mode & mask);
835 /* if we previously had any r bits set then leave them alone */
836 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
837 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
838 unixmode |= tmp;
841 /* if we previously had any w bits set then leave them alone
842 whilst adding in the new w bits, if the new mode is not rdonly */
843 if (!IS_DOS_READONLY(dosmode)) {
844 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
848 * From the chmod 2 man page:
850 * "If the calling process is not privileged, and the group of the file
851 * does not match the effective group ID of the process or one of its
852 * supplementary group IDs, the S_ISGID bit will be turned off, but
853 * this will not cause an error to be returned."
855 * Simply refuse to do the chmod in this case.
858 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
859 geteuid() != sec_initial_uid() &&
860 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
861 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
862 "set for directory %s\n",
863 smb_fname_str_dbg(smb_fname)));
864 errno = EPERM;
865 return -1;
868 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
869 if (ret == 0) {
870 if(!newfile || (lret != -1)) {
871 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
872 FILE_NOTIFY_CHANGE_ATTRIBUTES,
873 smb_fname->base_name);
875 smb_fname->st.st_ex_mode = unixmode;
876 return 0;
879 if((errno != EPERM) && (errno != EACCES))
880 return -1;
882 if(!lp_dos_filemode(SNUM(conn)))
883 return -1;
885 /* We want DOS semantics, ie allow non owner with write permission to change the
886 bits on a file. Just like file_ntimes below.
889 if (!can_write_to_file(conn, smb_fname)) {
890 errno = EACCES;
891 return -1;
895 * We need to get an open file handle to do the
896 * metadata operation under root.
899 status = get_file_handle_for_metadata(conn,
900 smb_fname,
901 &fsp,
902 &need_close);
903 if (!NT_STATUS_IS_OK(status)) {
904 errno = map_errno_from_nt_status(status);
905 return -1;
908 become_root();
909 ret = SMB_VFS_FCHMOD(fsp, unixmode);
910 unbecome_root();
911 if (need_close) {
912 close_file(NULL, fsp, NORMAL_CLOSE);
914 if (!newfile) {
915 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
916 FILE_NOTIFY_CHANGE_ATTRIBUTES,
917 smb_fname->base_name);
919 if (ret == 0) {
920 smb_fname->st.st_ex_mode = unixmode;
923 return( ret );
927 NTSTATUS file_set_sparse(connection_struct *conn,
928 files_struct *fsp,
929 bool sparse)
931 uint32_t old_dosmode;
932 uint32_t new_dosmode;
933 NTSTATUS status;
935 if (!CAN_WRITE(conn)) {
936 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
937 "on readonly share[%s]\n",
938 smb_fname_str_dbg(fsp->fsp_name),
939 sparse,
940 lp_servicename(talloc_tos(), SNUM(conn))));
941 return NT_STATUS_MEDIA_WRITE_PROTECTED;
944 if (!(fsp->access_mask & FILE_WRITE_DATA) &&
945 !(fsp->access_mask & FILE_WRITE_ATTRIBUTES)) {
946 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
947 "access_mask[0x%08X] - access denied\n",
948 smb_fname_str_dbg(fsp->fsp_name),
949 sparse,
950 fsp->access_mask));
951 return NT_STATUS_ACCESS_DENIED;
954 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
955 sparse, smb_fname_str_dbg(fsp->fsp_name)));
957 if (!lp_store_dos_attributes(SNUM(conn))) {
958 return NT_STATUS_INVALID_DEVICE_REQUEST;
961 status = vfs_stat_fsp(fsp);
962 if (!NT_STATUS_IS_OK(status)) {
963 return status;
966 old_dosmode = dos_mode(conn, fsp->fsp_name);
968 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
969 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
970 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
971 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
972 } else {
973 return NT_STATUS_OK;
976 /* Store the DOS attributes in an EA. */
977 if (!set_ea_dos_attribute(conn, fsp->fsp_name,
978 new_dosmode)) {
979 if (errno == 0) {
980 errno = EIO;
982 return map_nt_error_from_unix(errno);
985 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
986 FILE_NOTIFY_CHANGE_ATTRIBUTES,
987 fsp->fsp_name->base_name);
989 fsp->is_sparse = sparse;
991 return NT_STATUS_OK;
994 /*******************************************************************
995 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
996 than POSIX.
997 *******************************************************************/
999 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
1000 struct smb_file_time *ft)
1002 int ret = -1;
1004 errno = 0;
1006 DEBUG(6, ("file_ntime: actime: %s",
1007 time_to_asc(convert_timespec_to_time_t(ft->atime))));
1008 DEBUG(6, ("file_ntime: modtime: %s",
1009 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
1010 DEBUG(6, ("file_ntime: ctime: %s",
1011 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
1012 DEBUG(6, ("file_ntime: createtime: %s",
1013 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
1015 /* Don't update the time on read-only shares */
1016 /* We need this as set_filetime (which can be called on
1017 close and other paths) can end up calling this function
1018 without the NEED_WRITE protection. Found by :
1019 Leo Weppelman <leo@wau.mis.ah.nl>
1022 if (!CAN_WRITE(conn)) {
1023 return 0;
1026 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
1027 return 0;
1030 if((errno != EPERM) && (errno != EACCES)) {
1031 return -1;
1034 if(!lp_dos_filetimes(SNUM(conn))) {
1035 return -1;
1038 /* We have permission (given by the Samba admin) to
1039 break POSIX semantics and allow a user to change
1040 the time on a file they don't own but can write to
1041 (as DOS does).
1044 /* Check if we have write access. */
1045 if (can_write_to_file(conn, smb_fname)) {
1046 /* We are allowed to become root and change the filetime. */
1047 become_root();
1048 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1049 unbecome_root();
1052 return ret;
1055 /******************************************************************
1056 Force a "sticky" write time on a pathname. This will always be
1057 returned on all future write time queries and set on close.
1058 ******************************************************************/
1060 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1062 if (null_timespec(mtime)) {
1063 return true;
1066 if (!set_sticky_write_time(fileid, mtime)) {
1067 return false;
1070 return true;
1073 /******************************************************************
1074 Force a "sticky" write time on an fsp. This will always be
1075 returned on all future write time queries and set on close.
1076 ******************************************************************/
1078 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1080 if (null_timespec(mtime)) {
1081 return true;
1084 fsp->write_time_forced = true;
1085 TALLOC_FREE(fsp->update_write_time_event);
1087 return set_sticky_write_time_path(fsp->file_id, mtime);
1090 /******************************************************************
1091 Set a create time EA.
1092 ******************************************************************/
1094 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1095 const struct smb_filename *psmb_fname,
1096 struct timespec create_time)
1098 struct smb_filename *smb_fname;
1099 uint32_t dosmode;
1100 int ret;
1102 if (!lp_store_dos_attributes(SNUM(conn))) {
1103 return NT_STATUS_OK;
1106 smb_fname = synthetic_smb_fname(talloc_tos(), psmb_fname->base_name,
1107 NULL, &psmb_fname->st);
1109 if (smb_fname == NULL) {
1110 return NT_STATUS_NO_MEMORY;
1113 dosmode = dos_mode(conn, smb_fname);
1115 smb_fname->st.st_ex_btime = create_time;
1117 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1118 if (ret == -1) {
1119 map_nt_error_from_unix(errno);
1122 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1123 smb_fname_str_dbg(smb_fname)));
1125 return NT_STATUS_OK;
1128 /******************************************************************
1129 Return a create time.
1130 ******************************************************************/
1132 struct timespec get_create_timespec(connection_struct *conn,
1133 struct files_struct *fsp,
1134 const struct smb_filename *smb_fname)
1136 return smb_fname->st.st_ex_btime;
1139 /******************************************************************
1140 Return a change time (may look at EA in future).
1141 ******************************************************************/
1143 struct timespec get_change_timespec(connection_struct *conn,
1144 struct files_struct *fsp,
1145 const struct smb_filename *smb_fname)
1147 return smb_fname->st.st_ex_mtime;
1150 /****************************************************************************
1151 Get a real open file handle we can do meta-data operations on. As it's
1152 going to be used under root access only on meta-data we should look for
1153 any existing open file handle first, and use that in preference (also to
1154 avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1155 ****************************************************************************/
1157 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1158 struct smb_filename *smb_fname,
1159 files_struct **ret_fsp,
1160 bool *need_close)
1162 NTSTATUS status;
1163 files_struct *fsp;
1164 struct file_id file_id;
1166 *need_close = false;
1168 if (!VALID_STAT(smb_fname->st)) {
1169 return NT_STATUS_INVALID_PARAMETER;
1172 file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1174 for(fsp = file_find_di_first(conn->sconn, file_id);
1175 fsp;
1176 fsp = file_find_di_next(fsp)) {
1177 if (fsp->fh->fd != -1) {
1178 *ret_fsp = fsp;
1179 return NT_STATUS_OK;
1183 /* Opens an INTERNAL_OPEN_ONLY write handle. */
1184 status = SMB_VFS_CREATE_FILE(
1185 conn, /* conn */
1186 NULL, /* req */
1187 0, /* root_dir_fid */
1188 smb_fname, /* fname */
1189 FILE_WRITE_DATA, /* access_mask */
1190 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
1191 FILE_SHARE_DELETE),
1192 FILE_OPEN, /* create_disposition*/
1193 0, /* create_options */
1194 0, /* file_attributes */
1195 INTERNAL_OPEN_ONLY, /* oplock_request */
1196 0, /* allocation_size */
1197 0, /* private_flags */
1198 NULL, /* sd */
1199 NULL, /* ea_list */
1200 ret_fsp, /* result */
1201 NULL); /* pinfo */
1203 if (NT_STATUS_IS_OK(status)) {
1204 *need_close = true;
1206 return status;