dosmode: fix FSCTL_SET_SPARSE request validation
[Samba.git] / source3 / smbd / dosmode.c
blob1a930dee25f98f030ee07962b25265a6287c1054
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 = NULL;
93 NTSTATUS status;
95 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
96 smb_fname_str_dbg(smb_fname),
97 inherit_from_dir));
99 status = create_synthetic_smb_fname(talloc_tos(),
100 inherit_from_dir, NULL,
101 NULL, &smb_fname_parent);
102 if (!NT_STATUS_IS_OK(status)) {
103 DEBUG(1,("unix_mode(%s) failed, [dir %s]: %s\n",
104 smb_fname_str_dbg(smb_fname),
105 inherit_from_dir, nt_errstr(status)));
106 return(0);
109 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
110 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
111 smb_fname_str_dbg(smb_fname),
112 inherit_from_dir, strerror(errno)));
113 TALLOC_FREE(smb_fname_parent);
114 return(0); /* *** shouldn't happen! *** */
117 /* Save for later - but explicitly remove setuid bit for safety. */
118 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
119 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
120 smb_fname_str_dbg(smb_fname), (int)dir_mode));
121 /* Clear "result" */
122 result = 0;
123 TALLOC_FREE(smb_fname_parent);
126 if (IS_DOS_DIR(dosmode)) {
127 /* We never make directories read only for the owner as under DOS a user
128 can always create a file in a read-only directory. */
129 result |= (S_IFDIR | S_IWUSR);
131 if (dir_mode) {
132 /* Inherit mode of parent directory. */
133 result |= dir_mode;
134 } else {
135 /* Provisionally add all 'x' bits */
136 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
138 /* Apply directory mask */
139 result &= lp_dir_mask(SNUM(conn));
140 /* Add in force bits */
141 result |= lp_force_dir_mode(SNUM(conn));
143 } else {
144 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
145 result |= S_IXUSR;
147 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
148 result |= S_IXGRP;
150 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
151 result |= S_IXOTH;
153 if (dir_mode) {
154 /* Inherit 666 component of parent directory mode */
155 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
156 } else {
157 /* Apply mode mask */
158 result &= lp_create_mask(SNUM(conn));
159 /* Add in force bits */
160 result |= lp_force_create_mode(SNUM(conn));
164 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
165 (int)result));
166 return(result);
169 /****************************************************************************
170 Change a unix mode to a dos mode.
171 ****************************************************************************/
173 static uint32 dos_mode_from_sbuf(connection_struct *conn,
174 const struct smb_filename *smb_fname)
176 int result = 0;
177 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
179 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
180 /* if we can find out if a file is immutable we should report it r/o */
181 if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
182 result |= FILE_ATTRIBUTE_READONLY;
184 #endif
185 if (ro_opts == MAP_READONLY_YES) {
186 /* Original Samba method - map inverse of user "w" bit. */
187 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
188 result |= FILE_ATTRIBUTE_READONLY;
190 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
191 /* Check actual permissions for read-only. */
192 if (!can_write_to_file(conn, smb_fname)) {
193 result |= FILE_ATTRIBUTE_READONLY;
195 } /* Else never set the readonly bit. */
197 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
198 result |= FILE_ATTRIBUTE_ARCHIVE;
200 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
201 result |= FILE_ATTRIBUTE_SYSTEM;
203 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
204 result |= FILE_ATTRIBUTE_HIDDEN;
206 if (S_ISDIR(smb_fname->st.st_ex_mode))
207 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
209 result |= set_link_read_only_flag(&smb_fname->st);
211 DEBUG(8,("dos_mode_from_sbuf returning "));
213 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
214 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
215 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
216 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
217 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
219 DEBUG(8,("\n"));
220 return result;
223 /****************************************************************************
224 Get DOS attributes from an EA.
225 This can also pull the create time into the stat struct inside smb_fname.
226 ****************************************************************************/
228 static bool get_ea_dos_attribute(connection_struct *conn,
229 struct smb_filename *smb_fname,
230 uint32 *pattr)
232 struct xattr_DOSATTRIB dosattrib;
233 enum ndr_err_code ndr_err;
234 DATA_BLOB blob;
235 ssize_t sizeret;
236 fstring attrstr;
237 uint32_t dosattr;
239 if (!lp_store_dos_attributes(SNUM(conn))) {
240 return False;
243 /* Don't reset pattr to zero as we may already have filename-based attributes we
244 need to preserve. */
246 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
247 SAMBA_XATTR_DOS_ATTRIB, attrstr,
248 sizeof(attrstr));
249 if (sizeret == -1) {
250 if (errno == ENOSYS
251 #if defined(ENOTSUP)
252 || errno == ENOTSUP) {
253 #else
255 #endif
256 DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
257 "from EA on file %s: Error = %s\n",
258 smb_fname_str_dbg(smb_fname),
259 strerror(errno)));
260 set_store_dos_attributes(SNUM(conn), False);
262 return False;
265 blob.data = (uint8_t *)attrstr;
266 blob.length = sizeret;
268 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
269 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
271 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
272 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
273 "from EA on file %s: Error = %s\n",
274 smb_fname_str_dbg(smb_fname),
275 ndr_errstr(ndr_err)));
276 return false;
279 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
280 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
282 switch (dosattrib.version) {
283 case 0xFFFF:
284 dosattr = dosattrib.info.compatinfoFFFF.attrib;
285 break;
286 case 1:
287 dosattr = dosattrib.info.info1.attrib;
288 if (!null_nttime(dosattrib.info.info1.create_time)) {
289 struct timespec create_time =
290 nt_time_to_unix_timespec(
291 &dosattrib.info.info1.create_time);
293 update_stat_ex_create_time(&smb_fname->st,
294 create_time);
296 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
297 "set btime %s\n",
298 smb_fname_str_dbg(smb_fname),
299 time_to_asc(convert_timespec_to_time_t(
300 create_time)) ));
302 break;
303 case 2:
304 dosattr = dosattrib.info.oldinfo2.attrib;
305 /* Don't know what flags to check for this case. */
306 break;
307 case 3:
308 dosattr = dosattrib.info.info3.attrib;
309 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
310 !null_nttime(dosattrib.info.info3.create_time)) {
311 struct timespec create_time =
312 nt_time_to_unix_timespec(
313 &dosattrib.info.info3.create_time);
315 update_stat_ex_create_time(&smb_fname->st,
316 create_time);
318 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
319 "set btime %s\n",
320 smb_fname_str_dbg(smb_fname),
321 time_to_asc(convert_timespec_to_time_t(
322 create_time)) ));
324 break;
325 default:
326 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
327 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
328 attrstr));
329 return false;
332 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
333 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
335 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
336 *pattr = (uint32)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
338 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
340 if (dosattr & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
341 if (dosattr & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
342 if (dosattr & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
343 if (dosattr & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
344 if (dosattr & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
346 DEBUG(8,("\n"));
348 return True;
351 /****************************************************************************
352 Set DOS attributes in an EA.
353 Also sets the create time.
354 ****************************************************************************/
356 static bool set_ea_dos_attribute(connection_struct *conn,
357 struct smb_filename *smb_fname,
358 uint32 dosmode)
360 struct xattr_DOSATTRIB dosattrib;
361 enum ndr_err_code ndr_err;
362 DATA_BLOB blob;
364 if (!lp_store_dos_attributes(SNUM(conn))) {
365 return False;
368 ZERO_STRUCT(dosattrib);
369 ZERO_STRUCT(blob);
371 dosattrib.version = 3;
372 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
373 XATTR_DOSINFO_CREATE_TIME;
374 dosattrib.info.info3.attrib = dosmode;
375 unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
376 smb_fname->st.st_ex_btime);
378 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
379 (unsigned int)dosmode,
380 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
381 smb_fname_str_dbg(smb_fname) ));
383 ndr_err = ndr_push_struct_blob(
384 &blob, talloc_tos(), &dosattrib,
385 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
387 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
388 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
389 ndr_errstr(ndr_err)));
390 return false;
393 if (blob.data == NULL || blob.length == 0) {
394 return false;
397 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
398 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
399 0) == -1) {
400 bool ret = false;
401 bool need_close = false;
402 files_struct *fsp = NULL;
404 if((errno != EPERM) && (errno != EACCES)) {
405 if (errno == ENOSYS
406 #if defined(ENOTSUP)
407 || errno == ENOTSUP) {
408 #else
410 #endif
411 DEBUG(1,("set_ea_dos_attributes: Cannot set "
412 "attribute EA on file %s: Error = %s\n",
413 smb_fname_str_dbg(smb_fname),
414 strerror(errno) ));
415 set_store_dos_attributes(SNUM(conn), False);
417 return false;
420 /* We want DOS semantics, ie allow non owner with write permission to change the
421 bits on a file. Just like file_ntimes below.
424 /* Check if we have write access. */
425 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
426 return false;
428 if (!can_write_to_file(conn, smb_fname)) {
429 return false;
433 * We need to get an open file handle to do the
434 * metadata operation under root.
437 if (!NT_STATUS_IS_OK(get_file_handle_for_metadata(conn,
438 smb_fname,
439 &fsp,
440 &need_close))) {
441 return false;
444 become_root();
445 if (SMB_VFS_FSETXATTR(fsp,
446 SAMBA_XATTR_DOS_ATTRIB, blob.data,
447 blob.length, 0) == 0) {
448 ret = true;
450 unbecome_root();
451 if (need_close) {
452 close_file(NULL, fsp, NORMAL_CLOSE);
454 return ret;
456 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
457 (unsigned int)dosmode,
458 smb_fname_str_dbg(smb_fname)));
459 return true;
462 /****************************************************************************
463 Change a unix mode to a dos mode for an ms dfs link.
464 ****************************************************************************/
466 uint32 dos_mode_msdfs(connection_struct *conn,
467 const struct smb_filename *smb_fname)
469 uint32 result = 0;
471 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
473 if (!VALID_STAT(smb_fname->st)) {
474 return 0;
477 /* First do any modifications that depend on the path name. */
478 /* hide files with a name starting with a . */
479 if (lp_hide_dot_files(SNUM(conn))) {
480 const char *p = strrchr_m(smb_fname->base_name, '/');
481 if (p) {
482 p++;
483 } else {
484 p = smb_fname->base_name;
487 /* Only . and .. are not hidden. */
488 if (p[0] == '.' && !((p[1] == '\0') ||
489 (p[1] == '.' && p[2] == '\0'))) {
490 result |= FILE_ATTRIBUTE_HIDDEN;
494 result |= dos_mode_from_sbuf(conn, smb_fname);
496 /* Optimization : Only call is_hidden_path if it's not already
497 hidden. */
498 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
499 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
500 result |= FILE_ATTRIBUTE_HIDDEN;
503 if (result == 0) {
504 result = FILE_ATTRIBUTE_NORMAL;
507 result = filter_mode_by_protocol(result);
510 * Add in that it is a reparse point
512 result |= FILE_ATTRIBUTE_REPARSE_POINT;
514 DEBUG(8,("dos_mode_msdfs returning "));
516 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
517 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
518 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
519 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
520 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
521 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
523 DEBUG(8,("\n"));
525 return(result);
528 #ifdef HAVE_STAT_DOS_FLAGS
529 /****************************************************************************
530 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
531 ****************************************************************************/
533 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
535 uint32_t dos_stat_flags = 0;
537 if (dosmode & FILE_ATTRIBUTE_ARCHIVE)
538 dos_stat_flags |= UF_DOS_ARCHIVE;
539 if (dosmode & FILE_ATTRIBUTE_HIDDEN)
540 dos_stat_flags |= UF_DOS_HIDDEN;
541 if (dosmode & FILE_ATTRIBUTE_READONLY)
542 dos_stat_flags |= UF_DOS_RO;
543 if (dosmode & FILE_ATTRIBUTE_SYSTEM)
544 dos_stat_flags |= UF_DOS_SYSTEM;
545 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
546 dos_stat_flags |= UF_DOS_NOINDEX;
548 return dos_stat_flags;
551 /****************************************************************************
552 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
553 ****************************************************************************/
555 static bool get_stat_dos_flags(connection_struct *conn,
556 const struct smb_filename *smb_fname,
557 uint32_t *dosmode)
559 SMB_ASSERT(VALID_STAT(smb_fname->st));
560 SMB_ASSERT(dosmode);
562 if (!lp_store_dos_attributes(SNUM(conn))) {
563 return false;
566 DEBUG(5, ("Getting stat dos attributes for %s.\n",
567 smb_fname_str_dbg(smb_fname)));
569 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
570 *dosmode |= FILE_ATTRIBUTE_ARCHIVE;
571 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
572 *dosmode |= FILE_ATTRIBUTE_HIDDEN;
573 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
574 *dosmode |= FILE_ATTRIBUTE_READONLY;
575 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
576 *dosmode |= FILE_ATTRIBUTE_SYSTEM;
577 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
578 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
579 if (smb_fname->st.st_ex_flags & FILE_ATTRIBUTE_SPARSE)
580 *dosmode |= FILE_ATTRIBUTE_SPARSE;
581 if (S_ISDIR(smb_fname->st.st_ex_mode))
582 *dosmode |= FILE_ATTRIBUTE_DIRECTORY;
584 *dosmode |= set_link_read_only_flag(&smb_fname->st);
586 return true;
589 /****************************************************************************
590 Sets DOS attributes, stored in st_ex_flags of the inode.
591 ****************************************************************************/
593 static bool set_stat_dos_flags(connection_struct *conn,
594 const struct smb_filename *smb_fname,
595 uint32_t dosmode,
596 bool *attributes_changed)
598 uint32_t new_flags = 0;
599 int error = 0;
601 SMB_ASSERT(VALID_STAT(smb_fname->st));
602 SMB_ASSERT(attributes_changed);
604 *attributes_changed = false;
606 if (!lp_store_dos_attributes(SNUM(conn))) {
607 return false;
610 DEBUG(5, ("Setting stat dos attributes for %s.\n",
611 smb_fname_str_dbg(smb_fname)));
613 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
614 dos_attributes_to_stat_dos_flags(dosmode);
616 /* Return early if no flags changed. */
617 if (new_flags == smb_fname->st.st_ex_flags)
618 return true;
620 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
621 smb_fname->st.st_ex_flags));
623 /* Set new flags with chflags. */
624 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
625 if (error) {
626 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
627 "file %s! errno=%d\n", new_flags,
628 smb_fname_str_dbg(smb_fname), errno));
629 return false;
632 *attributes_changed = true;
633 return true;
635 #endif /* HAVE_STAT_DOS_FLAGS */
637 /****************************************************************************
638 Change a unix mode to a dos mode.
639 May also read the create timespec into the stat struct in smb_fname
640 if "store dos attributes" is true.
641 ****************************************************************************/
643 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
645 uint32 result = 0;
646 bool offline, used_stat_dos_flags = false;
648 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
650 if (!VALID_STAT(smb_fname->st)) {
651 return 0;
654 /* First do any modifications that depend on the path name. */
655 /* hide files with a name starting with a . */
656 if (lp_hide_dot_files(SNUM(conn))) {
657 const char *p = strrchr_m(smb_fname->base_name,'/');
658 if (p) {
659 p++;
660 } else {
661 p = smb_fname->base_name;
664 /* Only . and .. are not hidden. */
665 if (p[0] == '.' && !((p[1] == '\0') ||
666 (p[1] == '.' && p[2] == '\0'))) {
667 result |= FILE_ATTRIBUTE_HIDDEN;
671 #ifdef HAVE_STAT_DOS_FLAGS
672 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
673 #endif
674 if (!used_stat_dos_flags) {
675 /* Get the DOS attributes from an EA by preference. */
676 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
677 result |= dos_mode_from_sbuf(conn, smb_fname);
681 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
682 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
683 result |= FILE_ATTRIBUTE_OFFLINE;
686 /* Optimization : Only call is_hidden_path if it's not already
687 hidden. */
688 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
689 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
690 result |= FILE_ATTRIBUTE_HIDDEN;
693 if (result == 0) {
694 result = FILE_ATTRIBUTE_NORMAL;
697 result = filter_mode_by_protocol(result);
699 DEBUG(8,("dos_mode returning "));
701 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
702 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
703 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
704 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
705 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
706 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
708 DEBUG(8,("\n"));
710 return(result);
713 /*******************************************************************
714 chmod a file - but preserve some bits.
715 If "store dos attributes" is also set it will store the create time
716 from the stat struct in smb_fname (in NTTIME format) in the EA
717 attribute also.
718 ********************************************************************/
720 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
721 uint32 dosmode, const char *parent_dir, bool newfile)
723 int mask=0;
724 mode_t tmp;
725 mode_t unixmode;
726 int ret = -1, lret = -1;
727 uint32_t old_mode;
728 struct timespec new_create_timespec;
729 files_struct *fsp = NULL;
730 bool need_close = false;
731 NTSTATUS status;
733 if (!CAN_WRITE(conn)) {
734 errno = EROFS;
735 return -1;
738 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
739 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
741 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
742 dosmode, smb_fname_str_dbg(smb_fname)));
744 unixmode = smb_fname->st.st_ex_mode;
746 get_acl_group_bits(conn, smb_fname->base_name,
747 &smb_fname->st.st_ex_mode);
749 if (S_ISDIR(smb_fname->st.st_ex_mode))
750 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
751 else
752 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
754 new_create_timespec = smb_fname->st.st_ex_btime;
756 old_mode = dos_mode(conn, smb_fname);
758 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
759 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
760 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
761 if (lret == -1) {
762 DEBUG(0, ("set_dos_mode: client has asked to "
763 "set FILE_ATTRIBUTE_OFFLINE to "
764 "%s/%s but there was an error while "
765 "setting it or it is not "
766 "supported.\n", parent_dir,
767 smb_fname_str_dbg(smb_fname)));
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 (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
796 if (!newfile) {
797 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
798 FILE_NOTIFY_CHANGE_ATTRIBUTES,
799 smb_fname->base_name);
801 smb_fname->st.st_ex_mode = unixmode;
802 return 0;
805 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
807 /* preserve the s bits */
808 mask |= (S_ISUID | S_ISGID);
810 /* preserve the t bit */
811 #ifdef S_ISVTX
812 mask |= S_ISVTX;
813 #endif
815 /* possibly preserve the x bits */
816 if (!MAP_ARCHIVE(conn))
817 mask |= S_IXUSR;
818 if (!MAP_SYSTEM(conn))
819 mask |= S_IXGRP;
820 if (!MAP_HIDDEN(conn))
821 mask |= S_IXOTH;
823 unixmode |= (smb_fname->st.st_ex_mode & mask);
825 /* if we previously had any r bits set then leave them alone */
826 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
827 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
828 unixmode |= tmp;
831 /* if we previously had any w bits set then leave them alone
832 whilst adding in the new w bits, if the new mode is not rdonly */
833 if (!IS_DOS_READONLY(dosmode)) {
834 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
838 * From the chmod 2 man page:
840 * "If the calling process is not privileged, and the group of the file
841 * does not match the effective group ID of the process or one of its
842 * supplementary group IDs, the S_ISGID bit will be turned off, but
843 * this will not cause an error to be returned."
845 * Simply refuse to do the chmod in this case.
848 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
849 geteuid() != sec_initial_uid() &&
850 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
851 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
852 "set for directory %s\n",
853 smb_fname_str_dbg(smb_fname)));
854 errno = EPERM;
855 return -1;
858 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
859 if (ret == 0) {
860 if(!newfile || (lret != -1)) {
861 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
862 FILE_NOTIFY_CHANGE_ATTRIBUTES,
863 smb_fname->base_name);
865 smb_fname->st.st_ex_mode = unixmode;
866 return 0;
869 if((errno != EPERM) && (errno != EACCES))
870 return -1;
872 if(!lp_dos_filemode(SNUM(conn)))
873 return -1;
875 /* We want DOS semantics, ie allow non owner with write permission to change the
876 bits on a file. Just like file_ntimes below.
879 if (!can_write_to_file(conn, smb_fname)) {
880 errno = EACCES;
881 return -1;
885 * We need to get an open file handle to do the
886 * metadata operation under root.
889 status = get_file_handle_for_metadata(conn,
890 smb_fname,
891 &fsp,
892 &need_close);
893 if (!NT_STATUS_IS_OK(status)) {
894 errno = map_errno_from_nt_status(status);
895 return -1;
898 become_root();
899 ret = SMB_VFS_FCHMOD(fsp, unixmode);
900 unbecome_root();
901 if (need_close) {
902 close_file(NULL, fsp, NORMAL_CLOSE);
904 if (!newfile) {
905 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
906 FILE_NOTIFY_CHANGE_ATTRIBUTES,
907 smb_fname->base_name);
909 if (ret == 0) {
910 smb_fname->st.st_ex_mode = unixmode;
913 return( ret );
917 NTSTATUS file_set_sparse(connection_struct *conn,
918 files_struct *fsp,
919 bool sparse)
921 uint32_t old_dosmode;
922 uint32_t new_dosmode;
923 NTSTATUS status;
925 if (!CAN_WRITE(conn)) {
926 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
927 "on readonly share[%s]\n",
928 smb_fname_str_dbg(fsp->fsp_name),
929 sparse,
930 lp_servicename(talloc_tos(), SNUM(conn))));
931 return NT_STATUS_MEDIA_WRITE_PROTECTED;
934 if (!(fsp->access_mask & FILE_WRITE_DATA) &&
935 !(fsp->access_mask & FILE_WRITE_ATTRIBUTES)) {
936 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
937 "access_mask[0x%08X] - access denied\n",
938 smb_fname_str_dbg(fsp->fsp_name),
939 sparse,
940 fsp->access_mask));
941 return NT_STATUS_ACCESS_DENIED;
944 if (fsp->is_directory) {
945 DEBUG(9, ("invalid attempt to %s sparse flag on dir %s\n",
946 (sparse ? "set" : "clear"),
947 smb_fname_str_dbg(fsp->fsp_name)));
948 return NT_STATUS_INVALID_PARAMETER;
951 if (IS_IPC(conn) || IS_PRINT(conn)) {
952 DEBUG(9, ("attempt to %s sparse flag over invalid conn\n",
953 (sparse ? "set" : "clear")));
954 return NT_STATUS_INVALID_PARAMETER;
957 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
958 sparse, smb_fname_str_dbg(fsp->fsp_name)));
960 if (!lp_store_dos_attributes(SNUM(conn))) {
961 return NT_STATUS_INVALID_DEVICE_REQUEST;
964 status = vfs_stat_fsp(fsp);
965 if (!NT_STATUS_IS_OK(status)) {
966 return status;
969 old_dosmode = dos_mode(conn, fsp->fsp_name);
971 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
972 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
973 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
974 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
975 } else {
976 return NT_STATUS_OK;
979 /* Store the DOS attributes in an EA. */
980 if (!set_ea_dos_attribute(conn, fsp->fsp_name,
981 new_dosmode)) {
982 if (errno == 0) {
983 errno = EIO;
985 return map_nt_error_from_unix(errno);
988 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
989 FILE_NOTIFY_CHANGE_ATTRIBUTES,
990 fsp->fsp_name->base_name);
992 fsp->is_sparse = sparse;
994 return NT_STATUS_OK;
997 /*******************************************************************
998 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
999 than POSIX.
1000 *******************************************************************/
1002 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
1003 struct smb_file_time *ft)
1005 int ret = -1;
1007 errno = 0;
1009 DEBUG(6, ("file_ntime: actime: %s",
1010 time_to_asc(convert_timespec_to_time_t(ft->atime))));
1011 DEBUG(6, ("file_ntime: modtime: %s",
1012 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
1013 DEBUG(6, ("file_ntime: ctime: %s",
1014 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
1015 DEBUG(6, ("file_ntime: createtime: %s",
1016 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
1018 /* Don't update the time on read-only shares */
1019 /* We need this as set_filetime (which can be called on
1020 close and other paths) can end up calling this function
1021 without the NEED_WRITE protection. Found by :
1022 Leo Weppelman <leo@wau.mis.ah.nl>
1025 if (!CAN_WRITE(conn)) {
1026 return 0;
1029 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
1030 return 0;
1033 if((errno != EPERM) && (errno != EACCES)) {
1034 return -1;
1037 if(!lp_dos_filetimes(SNUM(conn))) {
1038 return -1;
1041 /* We have permission (given by the Samba admin) to
1042 break POSIX semantics and allow a user to change
1043 the time on a file they don't own but can write to
1044 (as DOS does).
1047 /* Check if we have write access. */
1048 if (can_write_to_file(conn, smb_fname)) {
1049 /* We are allowed to become root and change the filetime. */
1050 become_root();
1051 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1052 unbecome_root();
1055 return ret;
1058 /******************************************************************
1059 Force a "sticky" write time on a pathname. This will always be
1060 returned on all future write time queries and set on close.
1061 ******************************************************************/
1063 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1065 if (null_timespec(mtime)) {
1066 return true;
1069 if (!set_sticky_write_time(fileid, mtime)) {
1070 return false;
1073 return true;
1076 /******************************************************************
1077 Force a "sticky" write time on an fsp. This will always be
1078 returned on all future write time queries and set on close.
1079 ******************************************************************/
1081 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1083 if (null_timespec(mtime)) {
1084 return true;
1087 fsp->write_time_forced = true;
1088 TALLOC_FREE(fsp->update_write_time_event);
1090 return set_sticky_write_time_path(fsp->file_id, mtime);
1093 /******************************************************************
1094 Set a create time EA.
1095 ******************************************************************/
1097 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1098 const struct smb_filename *psmb_fname,
1099 struct timespec create_time)
1101 NTSTATUS status;
1102 struct smb_filename *smb_fname = NULL;
1103 uint32_t dosmode;
1104 int ret;
1106 if (!lp_store_dos_attributes(SNUM(conn))) {
1107 return NT_STATUS_OK;
1110 status = create_synthetic_smb_fname(talloc_tos(),
1111 psmb_fname->base_name,
1112 NULL, &psmb_fname->st,
1113 &smb_fname);
1115 if (!NT_STATUS_IS_OK(status)) {
1116 return status;
1119 dosmode = dos_mode(conn, smb_fname);
1121 smb_fname->st.st_ex_btime = create_time;
1123 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1124 if (ret == -1) {
1125 map_nt_error_from_unix(errno);
1128 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1129 smb_fname_str_dbg(smb_fname)));
1131 return NT_STATUS_OK;
1134 /******************************************************************
1135 Return a create time.
1136 ******************************************************************/
1138 struct timespec get_create_timespec(connection_struct *conn,
1139 struct files_struct *fsp,
1140 const struct smb_filename *smb_fname)
1142 return smb_fname->st.st_ex_btime;
1145 /******************************************************************
1146 Return a change time (may look at EA in future).
1147 ******************************************************************/
1149 struct timespec get_change_timespec(connection_struct *conn,
1150 struct files_struct *fsp,
1151 const struct smb_filename *smb_fname)
1153 return smb_fname->st.st_ex_mtime;
1156 /****************************************************************************
1157 Get a real open file handle we can do meta-data operations on. As it's
1158 going to be used under root access only on meta-data we should look for
1159 any existing open file handle first, and use that in preference (also to
1160 avoid kernel self-oplock breaks). If not use an INTERNAL_OPEN_ONLY handle.
1161 ****************************************************************************/
1163 static NTSTATUS get_file_handle_for_metadata(connection_struct *conn,
1164 struct smb_filename *smb_fname,
1165 files_struct **ret_fsp,
1166 bool *need_close)
1168 NTSTATUS status;
1169 files_struct *fsp;
1170 struct file_id file_id;
1172 *need_close = false;
1174 if (!VALID_STAT(smb_fname->st)) {
1175 return NT_STATUS_INVALID_PARAMETER;
1178 file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
1180 for(fsp = file_find_di_first(conn->sconn, file_id);
1181 fsp;
1182 fsp = file_find_di_next(fsp)) {
1183 if (fsp->fh->fd != -1) {
1184 *ret_fsp = fsp;
1185 return NT_STATUS_OK;
1189 /* Opens an INTERNAL_OPEN_ONLY write handle. */
1190 status = SMB_VFS_CREATE_FILE(
1191 conn, /* conn */
1192 NULL, /* req */
1193 0, /* root_dir_fid */
1194 smb_fname, /* fname */
1195 FILE_WRITE_DATA, /* access_mask */
1196 (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */
1197 FILE_SHARE_DELETE),
1198 FILE_OPEN, /* create_disposition*/
1199 0, /* create_options */
1200 0, /* file_attributes */
1201 INTERNAL_OPEN_ONLY, /* oplock_request */
1202 0, /* allocation_size */
1203 0, /* private_flags */
1204 NULL, /* sd */
1205 NULL, /* ea_list */
1206 ret_fsp, /* result */
1207 NULL); /* pinfo */
1209 if (NT_STATUS_IS_OK(status)) {
1210 *need_close = true;
1212 return status;