s4:lib/socket: simplify iface_list_wildcard() and its callers
[Samba.git] / source3 / smbd / dosmode.c
blob831f6d0b6dec929dac8f675ebfbbcb803b8b1522
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 "librpc/gen_ndr/ioctl.h"
25 #include "../libcli/security/security.h"
26 #include "smbd/smbd.h"
27 #include "lib/param/loadparm.h"
29 static void dos_mode_debug_print(uint32_t mode)
31 DEBUG(8,("dos_mode returning "));
33 if (mode & FILE_ATTRIBUTE_HIDDEN) {
34 DEBUG(8, ("h"));
36 if (mode & FILE_ATTRIBUTE_READONLY) {
37 DEBUG(8, ("r"));
39 if (mode & FILE_ATTRIBUTE_SYSTEM) {
40 DEBUG(8, ("s"));
42 if (mode & FILE_ATTRIBUTE_DIRECTORY) {
43 DEBUG(8, ("d"));
45 if (mode & FILE_ATTRIBUTE_ARCHIVE) {
46 DEBUG(8, ("a"));
48 if (mode & FILE_ATTRIBUTE_SPARSE) {
49 DEBUG(8, ("[sparse]"));
51 if (mode & FILE_ATTRIBUTE_OFFLINE) {
52 DEBUG(8, ("[offline]"));
54 if (mode & FILE_ATTRIBUTE_COMPRESSED) {
55 DEBUG(8, ("[compressed]"));
58 DEBUG(8,("\n"));
61 static uint32_t filter_mode_by_protocol(uint32_t mode)
63 if (get_Protocol() <= PROTOCOL_LANMAN2) {
64 DEBUG(10,("filter_mode_by_protocol: "
65 "filtering result 0x%x to 0x%x\n",
66 (unsigned int)mode,
67 (unsigned int)(mode & 0x3f) ));
68 mode &= 0x3f;
70 return mode;
73 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
75 #ifdef S_ISLNK
76 #if LINKS_READ_ONLY
77 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
78 return FILE_ATTRIBUTE_READONLY;
79 #endif
80 #endif
81 return 0;
84 /****************************************************************************
85 Change a dos mode to a unix mode.
86 Base permission for files:
87 if creating file and inheriting (i.e. parent_dir != NULL)
88 apply read/write bits from parent directory.
89 else
90 everybody gets read bit set
91 dos readonly is represented in unix by removing everyone's write bit
92 dos archive is represented in unix by the user's execute bit
93 dos system is represented in unix by the group's execute bit
94 dos hidden is represented in unix by the other's execute bit
95 if !inheriting {
96 Then apply create mask,
97 then add force bits.
99 Base permission for directories:
100 dos directory is represented in unix by unix's dir bit and the exec bit
101 if !inheriting {
102 Then apply create mask,
103 then add force bits.
105 ****************************************************************************/
107 mode_t unix_mode(connection_struct *conn, int dosmode,
108 const struct smb_filename *smb_fname,
109 const char *inherit_from_dir)
111 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
112 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
113 * inheriting. */
115 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
116 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
119 if ((inherit_from_dir != NULL) && lp_inherit_permissions(SNUM(conn))) {
120 struct smb_filename *smb_fname_parent;
122 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
123 smb_fname_str_dbg(smb_fname),
124 inherit_from_dir));
126 smb_fname_parent = synthetic_smb_fname(
127 talloc_tos(), inherit_from_dir, NULL, NULL);
128 if (smb_fname_parent == NULL) {
129 DEBUG(1,("unix_mode(%s) failed, [dir %s]: No memory\n",
130 smb_fname_str_dbg(smb_fname),
131 inherit_from_dir));
132 return(0);
135 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
136 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
137 smb_fname_str_dbg(smb_fname),
138 inherit_from_dir, strerror(errno)));
139 TALLOC_FREE(smb_fname_parent);
140 return(0); /* *** shouldn't happen! *** */
143 /* Save for later - but explicitly remove setuid bit for safety. */
144 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
145 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
146 smb_fname_str_dbg(smb_fname), (int)dir_mode));
147 /* Clear "result" */
148 result = 0;
149 TALLOC_FREE(smb_fname_parent);
152 if (IS_DOS_DIR(dosmode)) {
153 /* We never make directories read only for the owner as under DOS a user
154 can always create a file in a read-only directory. */
155 result |= (S_IFDIR | S_IWUSR);
157 if (dir_mode) {
158 /* Inherit mode of parent directory. */
159 result |= dir_mode;
160 } else {
161 /* Provisionally add all 'x' bits */
162 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
164 /* Apply directory mask */
165 result &= lp_directory_mask(SNUM(conn));
166 /* Add in force bits */
167 result |= lp_force_directory_mode(SNUM(conn));
169 } else {
170 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
171 result |= S_IXUSR;
173 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
174 result |= S_IXGRP;
176 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
177 result |= S_IXOTH;
179 if (dir_mode) {
180 /* Inherit 666 component of parent directory mode */
181 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
182 } else {
183 /* Apply mode mask */
184 result &= lp_create_mask(SNUM(conn));
185 /* Add in force bits */
186 result |= lp_force_create_mode(SNUM(conn));
190 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
191 (int)result));
192 return(result);
195 /****************************************************************************
196 Change a unix mode to a dos mode.
197 ****************************************************************************/
199 static uint32 dos_mode_from_sbuf(connection_struct *conn,
200 const struct smb_filename *smb_fname)
202 int result = 0;
203 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
205 #if defined(UF_IMMUTABLE) && defined(SF_IMMUTABLE)
206 /* if we can find out if a file is immutable we should report it r/o */
207 if (smb_fname->st.st_ex_flags & (UF_IMMUTABLE | SF_IMMUTABLE)) {
208 result |= FILE_ATTRIBUTE_READONLY;
210 #endif
211 if (ro_opts == MAP_READONLY_YES) {
212 /* Original Samba method - map inverse of user "w" bit. */
213 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
214 result |= FILE_ATTRIBUTE_READONLY;
216 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
217 /* Check actual permissions for read-only. */
218 if (!can_write_to_file(conn, smb_fname)) {
219 result |= FILE_ATTRIBUTE_READONLY;
221 } /* Else never set the readonly bit. */
223 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
224 result |= FILE_ATTRIBUTE_ARCHIVE;
226 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
227 result |= FILE_ATTRIBUTE_SYSTEM;
229 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
230 result |= FILE_ATTRIBUTE_HIDDEN;
232 if (S_ISDIR(smb_fname->st.st_ex_mode))
233 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
235 result |= set_link_read_only_flag(&smb_fname->st);
237 DEBUG(8,("dos_mode_from_sbuf returning "));
239 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
240 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
241 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
242 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
243 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
245 DEBUG(8,("\n"));
246 return result;
249 /****************************************************************************
250 Get DOS attributes from an EA.
251 This can also pull the create time into the stat struct inside smb_fname.
252 ****************************************************************************/
254 static bool get_ea_dos_attribute(connection_struct *conn,
255 struct smb_filename *smb_fname,
256 uint32 *pattr)
258 struct xattr_DOSATTRIB dosattrib;
259 enum ndr_err_code ndr_err;
260 DATA_BLOB blob;
261 ssize_t sizeret;
262 fstring attrstr;
263 uint32_t dosattr;
265 if (!lp_store_dos_attributes(SNUM(conn))) {
266 return False;
269 /* Don't reset pattr to zero as we may already have filename-based attributes we
270 need to preserve. */
272 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
273 SAMBA_XATTR_DOS_ATTRIB, attrstr,
274 sizeof(attrstr));
275 if (sizeret == -1) {
276 if (errno == ENOSYS
277 #if defined(ENOTSUP)
278 || errno == ENOTSUP) {
279 #else
281 #endif
282 DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
283 "from EA on file %s: Error = %s\n",
284 smb_fname_str_dbg(smb_fname),
285 strerror(errno)));
286 set_store_dos_attributes(SNUM(conn), False);
288 return False;
291 blob.data = (uint8_t *)attrstr;
292 blob.length = sizeret;
294 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
295 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
297 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
298 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
299 "from EA on file %s: Error = %s\n",
300 smb_fname_str_dbg(smb_fname),
301 ndr_errstr(ndr_err)));
302 return false;
305 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
306 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
308 switch (dosattrib.version) {
309 case 0xFFFF:
310 dosattr = dosattrib.info.compatinfoFFFF.attrib;
311 break;
312 case 1:
313 dosattr = dosattrib.info.info1.attrib;
314 if (!null_nttime(dosattrib.info.info1.create_time)) {
315 struct timespec create_time =
316 nt_time_to_unix_timespec(
317 &dosattrib.info.info1.create_time);
319 update_stat_ex_create_time(&smb_fname->st,
320 create_time);
322 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
323 "set btime %s\n",
324 smb_fname_str_dbg(smb_fname),
325 time_to_asc(convert_timespec_to_time_t(
326 create_time)) ));
328 break;
329 case 2:
330 dosattr = dosattrib.info.oldinfo2.attrib;
331 /* Don't know what flags to check for this case. */
332 break;
333 case 3:
334 dosattr = dosattrib.info.info3.attrib;
335 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
336 !null_nttime(dosattrib.info.info3.create_time)) {
337 struct timespec create_time =
338 nt_time_to_unix_timespec(
339 &dosattrib.info.info3.create_time);
341 update_stat_ex_create_time(&smb_fname->st,
342 create_time);
344 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
345 "set btime %s\n",
346 smb_fname_str_dbg(smb_fname),
347 time_to_asc(convert_timespec_to_time_t(
348 create_time)) ));
350 break;
351 default:
352 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
353 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
354 attrstr));
355 return false;
358 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
359 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
361 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
362 *pattr = (uint32)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
364 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
366 if (dosattr & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
367 if (dosattr & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
368 if (dosattr & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
369 if (dosattr & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
370 if (dosattr & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
372 DEBUG(8,("\n"));
374 return True;
377 /****************************************************************************
378 Set DOS attributes in an EA.
379 Also sets the create time.
380 ****************************************************************************/
382 static bool set_ea_dos_attribute(connection_struct *conn,
383 struct smb_filename *smb_fname,
384 uint32 dosmode)
386 struct xattr_DOSATTRIB dosattrib;
387 enum ndr_err_code ndr_err;
388 DATA_BLOB blob;
390 ZERO_STRUCT(dosattrib);
391 ZERO_STRUCT(blob);
393 dosattrib.version = 3;
394 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
395 XATTR_DOSINFO_CREATE_TIME;
396 dosattrib.info.info3.attrib = dosmode;
397 unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
398 smb_fname->st.st_ex_btime);
400 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
401 (unsigned int)dosmode,
402 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
403 smb_fname_str_dbg(smb_fname) ));
405 ndr_err = ndr_push_struct_blob(
406 &blob, talloc_tos(), &dosattrib,
407 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
409 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
410 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
411 ndr_errstr(ndr_err)));
412 return false;
415 if (blob.data == NULL || blob.length == 0) {
416 return false;
419 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
420 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
421 0) == -1) {
422 bool ret = false;
423 files_struct *fsp = NULL;
425 if((errno != EPERM) && (errno != EACCES)) {
426 if (errno == ENOSYS
427 #if defined(ENOTSUP)
428 || errno == ENOTSUP) {
429 #else
431 #endif
432 DEBUG(1,("set_ea_dos_attributes: Cannot set "
433 "attribute EA on file %s: Error = %s\n",
434 smb_fname_str_dbg(smb_fname),
435 strerror(errno) ));
436 set_store_dos_attributes(SNUM(conn), False);
438 return false;
441 /* We want DOS semantics, ie allow non owner with write permission to change the
442 bits on a file. Just like file_ntimes below.
445 /* Check if we have write access. */
446 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
447 return false;
449 if (!can_write_to_file(conn, smb_fname)) {
450 return false;
454 * We need to open the file with write access whilst
455 * still in our current user context. This ensures we
456 * are not violating security in doing the setxattr.
459 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
460 &fsp)))
461 return false;
462 become_root();
463 if (SMB_VFS_FSETXATTR(fsp,
464 SAMBA_XATTR_DOS_ATTRIB, blob.data,
465 blob.length, 0) == 0) {
466 ret = true;
468 unbecome_root();
469 close_file(NULL, fsp, NORMAL_CLOSE);
470 return ret;
472 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
473 (unsigned int)dosmode,
474 smb_fname_str_dbg(smb_fname)));
475 return true;
478 /****************************************************************************
479 Change a unix mode to a dos mode for an ms dfs link.
480 ****************************************************************************/
482 uint32 dos_mode_msdfs(connection_struct *conn,
483 const struct smb_filename *smb_fname)
485 uint32 result = 0;
487 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
489 if (!VALID_STAT(smb_fname->st)) {
490 return 0;
493 /* First do any modifications that depend on the path name. */
494 /* hide files with a name starting with a . */
495 if (lp_hide_dot_files(SNUM(conn))) {
496 const char *p = strrchr_m(smb_fname->base_name, '/');
497 if (p) {
498 p++;
499 } else {
500 p = smb_fname->base_name;
503 /* Only . and .. are not hidden. */
504 if (p[0] == '.' && !((p[1] == '\0') ||
505 (p[1] == '.' && p[2] == '\0'))) {
506 result |= FILE_ATTRIBUTE_HIDDEN;
510 result |= dos_mode_from_sbuf(conn, smb_fname);
512 /* Optimization : Only call is_hidden_path if it's not already
513 hidden. */
514 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
515 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
516 result |= FILE_ATTRIBUTE_HIDDEN;
519 if (result == 0) {
520 result = FILE_ATTRIBUTE_NORMAL;
523 result = filter_mode_by_protocol(result);
526 * Add in that it is a reparse point
528 result |= FILE_ATTRIBUTE_REPARSE_POINT;
530 DEBUG(8,("dos_mode_msdfs returning "));
532 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
533 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
534 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
535 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
536 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
537 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
539 DEBUG(8,("\n"));
541 return(result);
544 #ifdef HAVE_STAT_DOS_FLAGS
545 /****************************************************************************
546 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
547 ****************************************************************************/
549 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
551 uint32_t dos_stat_flags = 0;
553 if (dosmode & FILE_ATTRIBUTE_ARCHIVE)
554 dos_stat_flags |= UF_DOS_ARCHIVE;
555 if (dosmode & FILE_ATTRIBUTE_HIDDEN)
556 dos_stat_flags |= UF_DOS_HIDDEN;
557 if (dosmode & FILE_ATTRIBUTE_READONLY)
558 dos_stat_flags |= UF_DOS_RO;
559 if (dosmode & FILE_ATTRIBUTE_SYSTEM)
560 dos_stat_flags |= UF_DOS_SYSTEM;
561 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
562 dos_stat_flags |= UF_DOS_NOINDEX;
564 return dos_stat_flags;
567 /****************************************************************************
568 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
569 ****************************************************************************/
571 static bool get_stat_dos_flags(connection_struct *conn,
572 const struct smb_filename *smb_fname,
573 uint32_t *dosmode)
575 SMB_ASSERT(VALID_STAT(smb_fname->st));
576 SMB_ASSERT(dosmode);
578 if (!lp_store_dos_attributes(SNUM(conn))) {
579 return false;
582 DEBUG(5, ("Getting stat dos attributes for %s.\n",
583 smb_fname_str_dbg(smb_fname)));
585 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
586 *dosmode |= FILE_ATTRIBUTE_ARCHIVE;
587 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
588 *dosmode |= FILE_ATTRIBUTE_HIDDEN;
589 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
590 *dosmode |= FILE_ATTRIBUTE_READONLY;
591 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
592 *dosmode |= FILE_ATTRIBUTE_SYSTEM;
593 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
594 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
595 if (smb_fname->st.st_ex_flags & FILE_ATTRIBUTE_SPARSE)
596 *dosmode |= FILE_ATTRIBUTE_SPARSE;
597 if (S_ISDIR(smb_fname->st.st_ex_mode))
598 *dosmode |= FILE_ATTRIBUTE_DIRECTORY;
600 *dosmode |= set_link_read_only_flag(&smb_fname->st);
602 return true;
605 /****************************************************************************
606 Sets DOS attributes, stored in st_ex_flags of the inode.
607 ****************************************************************************/
609 static bool set_stat_dos_flags(connection_struct *conn,
610 const struct smb_filename *smb_fname,
611 uint32_t dosmode,
612 bool *attributes_changed)
614 uint32_t new_flags = 0;
615 int error = 0;
617 SMB_ASSERT(VALID_STAT(smb_fname->st));
618 SMB_ASSERT(attributes_changed);
620 *attributes_changed = false;
622 if (!lp_store_dos_attributes(SNUM(conn))) {
623 return false;
626 DEBUG(5, ("Setting stat dos attributes for %s.\n",
627 smb_fname_str_dbg(smb_fname)));
629 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
630 dos_attributes_to_stat_dos_flags(dosmode);
632 /* Return early if no flags changed. */
633 if (new_flags == smb_fname->st.st_ex_flags)
634 return true;
636 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
637 smb_fname->st.st_ex_flags));
639 /* Set new flags with chflags. */
640 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
641 if (error) {
642 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
643 "file %s! errno=%d\n", new_flags,
644 smb_fname_str_dbg(smb_fname), errno));
645 return false;
648 *attributes_changed = true;
649 return true;
651 #endif /* HAVE_STAT_DOS_FLAGS */
654 * check whether a file or directory is flagged as compressed.
656 static NTSTATUS dos_mode_check_compressed(connection_struct *conn,
657 struct smb_filename *smb_fname,
658 bool *is_compressed)
660 NTSTATUS status;
661 uint16_t compression_fmt;
662 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
663 if (tmp_ctx == NULL) {
664 status = NT_STATUS_NO_MEMORY;
665 goto err_out;
668 status = SMB_VFS_GET_COMPRESSION(conn, tmp_ctx, NULL, smb_fname,
669 &compression_fmt);
670 if (!NT_STATUS_IS_OK(status)) {
671 goto err_ctx_free;
674 if (compression_fmt == COMPRESSION_FORMAT_LZNT1) {
675 *is_compressed = true;
676 } else {
677 *is_compressed = false;
679 status = NT_STATUS_OK;
681 err_ctx_free:
682 talloc_free(tmp_ctx);
683 err_out:
684 return status;
687 /****************************************************************************
688 Change a unix mode to a dos mode.
689 May also read the create timespec into the stat struct in smb_fname
690 if "store dos attributes" is true.
691 ****************************************************************************/
693 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
695 uint32 result = 0;
696 bool offline, used_stat_dos_flags = false;
698 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
700 if (!VALID_STAT(smb_fname->st)) {
701 return 0;
704 /* First do any modifications that depend on the path name. */
705 /* hide files with a name starting with a . */
706 if (lp_hide_dot_files(SNUM(conn))) {
707 const char *p = strrchr_m(smb_fname->base_name,'/');
708 if (p) {
709 p++;
710 } else {
711 p = smb_fname->base_name;
714 /* Only . and .. are not hidden. */
715 if (p[0] == '.' && !((p[1] == '\0') ||
716 (p[1] == '.' && p[2] == '\0'))) {
717 result |= FILE_ATTRIBUTE_HIDDEN;
721 #ifdef HAVE_STAT_DOS_FLAGS
722 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
723 #endif
724 if (!used_stat_dos_flags) {
725 /* Get the DOS attributes from an EA by preference. */
726 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
727 result |= dos_mode_from_sbuf(conn, smb_fname);
731 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
732 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
733 result |= FILE_ATTRIBUTE_OFFLINE;
736 if (conn->fs_capabilities & FILE_FILE_COMPRESSION) {
737 bool compressed = false;
738 NTSTATUS status = dos_mode_check_compressed(conn, smb_fname,
739 &compressed);
740 if (NT_STATUS_IS_OK(status) && compressed) {
741 result |= FILE_ATTRIBUTE_COMPRESSED;
745 /* Optimization : Only call is_hidden_path if it's not already
746 hidden. */
747 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
748 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
749 result |= FILE_ATTRIBUTE_HIDDEN;
752 if (result == 0) {
753 result = FILE_ATTRIBUTE_NORMAL;
756 result = filter_mode_by_protocol(result);
758 dos_mode_debug_print(result);
760 return result;
763 /*******************************************************************
764 chmod a file - but preserve some bits.
765 If "store dos attributes" is also set it will store the create time
766 from the stat struct in smb_fname (in NTTIME format) in the EA
767 attribute also.
768 ********************************************************************/
770 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
771 uint32 dosmode, const char *parent_dir, bool newfile)
773 int mask=0;
774 mode_t tmp;
775 mode_t unixmode;
776 int ret = -1, lret = -1;
777 uint32_t old_mode;
778 struct timespec new_create_timespec;
779 files_struct *fsp = NULL;
781 if (!CAN_WRITE(conn)) {
782 errno = EROFS;
783 return -1;
786 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
787 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
789 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
790 dosmode, smb_fname_str_dbg(smb_fname)));
792 unixmode = smb_fname->st.st_ex_mode;
794 get_acl_group_bits(conn, smb_fname->base_name,
795 &smb_fname->st.st_ex_mode);
797 if (S_ISDIR(smb_fname->st.st_ex_mode))
798 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
799 else
800 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
802 new_create_timespec = smb_fname->st.st_ex_btime;
804 old_mode = dos_mode(conn, smb_fname);
806 if ((dosmode & FILE_ATTRIBUTE_OFFLINE) &&
807 !(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
808 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
809 if (lret == -1) {
810 if (errno == ENOTSUP) {
811 DEBUG(10, ("Setting FILE_ATTRIBUTE_OFFLINE for "
812 "%s/%s is not supported.\n",
813 parent_dir,
814 smb_fname_str_dbg(smb_fname)));
815 } else {
816 DEBUG(0, ("An error occurred while setting "
817 "FILE_ATTRIBUTE_OFFLINE for "
818 "%s/%s: %s", parent_dir,
819 smb_fname_str_dbg(smb_fname),
820 strerror(errno)));
825 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
826 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
828 smb_fname->st.st_ex_btime = new_create_timespec;
830 #ifdef HAVE_STAT_DOS_FLAGS
832 bool attributes_changed;
834 if (set_stat_dos_flags(conn, smb_fname, dosmode,
835 &attributes_changed))
837 if (!newfile && attributes_changed) {
838 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
839 FILE_NOTIFY_CHANGE_ATTRIBUTES,
840 smb_fname->base_name);
842 smb_fname->st.st_ex_mode = unixmode;
843 return 0;
846 #endif
847 /* Store the DOS attributes in an EA by preference. */
848 if (lp_store_dos_attributes(SNUM(conn))) {
850 * Don't fall back to using UNIX modes. Finally
851 * follow the smb.conf manpage.
853 if (!set_ea_dos_attribute(conn, smb_fname, dosmode)) {
854 return -1;
856 if (!newfile) {
857 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
858 FILE_NOTIFY_CHANGE_ATTRIBUTES,
859 smb_fname->base_name);
861 smb_fname->st.st_ex_mode = unixmode;
862 return 0;
865 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
867 /* preserve the file type bits */
868 mask |= S_IFMT;
870 /* preserve the s bits */
871 mask |= (S_ISUID | S_ISGID);
873 /* preserve the t bit */
874 #ifdef S_ISVTX
875 mask |= S_ISVTX;
876 #endif
878 /* possibly preserve the x bits */
879 if (!MAP_ARCHIVE(conn))
880 mask |= S_IXUSR;
881 if (!MAP_SYSTEM(conn))
882 mask |= S_IXGRP;
883 if (!MAP_HIDDEN(conn))
884 mask |= S_IXOTH;
886 unixmode |= (smb_fname->st.st_ex_mode & mask);
888 /* if we previously had any r bits set then leave them alone */
889 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
890 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
891 unixmode |= tmp;
894 /* if we previously had any w bits set then leave them alone
895 whilst adding in the new w bits, if the new mode is not rdonly */
896 if (!IS_DOS_READONLY(dosmode)) {
897 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
901 * From the chmod 2 man page:
903 * "If the calling process is not privileged, and the group of the file
904 * does not match the effective group ID of the process or one of its
905 * supplementary group IDs, the S_ISGID bit will be turned off, but
906 * this will not cause an error to be returned."
908 * Simply refuse to do the chmod in this case.
911 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
912 geteuid() != sec_initial_uid() &&
913 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
914 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
915 "set for directory %s\n",
916 smb_fname_str_dbg(smb_fname)));
917 errno = EPERM;
918 return -1;
921 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
922 if (ret == 0) {
923 if(!newfile || (lret != -1)) {
924 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
925 FILE_NOTIFY_CHANGE_ATTRIBUTES,
926 smb_fname->base_name);
928 smb_fname->st.st_ex_mode = unixmode;
929 return 0;
932 if((errno != EPERM) && (errno != EACCES))
933 return -1;
935 if(!lp_dos_filemode(SNUM(conn)))
936 return -1;
938 /* We want DOS semantics, ie allow non owner with write permission to change the
939 bits on a file. Just like file_ntimes below.
942 if (!can_write_to_file(conn, smb_fname)) {
943 errno = EACCES;
944 return -1;
948 * We need to open the file with write access whilst
949 * still in our current user context. This ensures we
950 * are not violating security in doing the fchmod.
952 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
953 &fsp)))
954 return -1;
955 become_root();
956 ret = SMB_VFS_FCHMOD(fsp, unixmode);
957 unbecome_root();
958 close_file(NULL, fsp, NORMAL_CLOSE);
959 if (!newfile) {
960 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
961 FILE_NOTIFY_CHANGE_ATTRIBUTES,
962 smb_fname->base_name);
964 if (ret == 0) {
965 smb_fname->st.st_ex_mode = unixmode;
968 return( ret );
972 NTSTATUS file_set_sparse(connection_struct *conn,
973 files_struct *fsp,
974 bool sparse)
976 uint32_t old_dosmode;
977 uint32_t new_dosmode;
978 NTSTATUS status;
980 if (!CAN_WRITE(conn)) {
981 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
982 "on readonly share[%s]\n",
983 smb_fname_str_dbg(fsp->fsp_name),
984 sparse,
985 lp_servicename(talloc_tos(), SNUM(conn))));
986 return NT_STATUS_MEDIA_WRITE_PROTECTED;
989 if (!(fsp->access_mask & FILE_WRITE_DATA) &&
990 !(fsp->access_mask & FILE_WRITE_ATTRIBUTES)) {
991 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
992 "access_mask[0x%08X] - access denied\n",
993 smb_fname_str_dbg(fsp->fsp_name),
994 sparse,
995 fsp->access_mask));
996 return NT_STATUS_ACCESS_DENIED;
999 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
1000 sparse, smb_fname_str_dbg(fsp->fsp_name)));
1002 if (!lp_store_dos_attributes(SNUM(conn))) {
1003 return NT_STATUS_INVALID_DEVICE_REQUEST;
1006 status = vfs_stat_fsp(fsp);
1007 if (!NT_STATUS_IS_OK(status)) {
1008 return status;
1011 old_dosmode = dos_mode(conn, fsp->fsp_name);
1013 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1014 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
1015 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
1016 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
1017 } else {
1018 return NT_STATUS_OK;
1021 /* Store the DOS attributes in an EA. */
1022 if (!set_ea_dos_attribute(conn, fsp->fsp_name,
1023 new_dosmode)) {
1024 if (errno == 0) {
1025 errno = EIO;
1027 return map_nt_error_from_unix(errno);
1030 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
1031 FILE_NOTIFY_CHANGE_ATTRIBUTES,
1032 fsp->fsp_name->base_name);
1034 fsp->is_sparse = sparse;
1036 return NT_STATUS_OK;
1039 /*******************************************************************
1040 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
1041 than POSIX.
1042 *******************************************************************/
1044 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
1045 struct smb_file_time *ft)
1047 int ret = -1;
1049 errno = 0;
1051 DEBUG(6, ("file_ntime: actime: %s",
1052 time_to_asc(convert_timespec_to_time_t(ft->atime))));
1053 DEBUG(6, ("file_ntime: modtime: %s",
1054 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
1055 DEBUG(6, ("file_ntime: ctime: %s",
1056 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
1057 DEBUG(6, ("file_ntime: createtime: %s",
1058 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
1060 /* Don't update the time on read-only shares */
1061 /* We need this as set_filetime (which can be called on
1062 close and other paths) can end up calling this function
1063 without the NEED_WRITE protection. Found by :
1064 Leo Weppelman <leo@wau.mis.ah.nl>
1067 if (!CAN_WRITE(conn)) {
1068 return 0;
1071 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
1072 return 0;
1075 if((errno != EPERM) && (errno != EACCES)) {
1076 return -1;
1079 if(!lp_dos_filetimes(SNUM(conn))) {
1080 return -1;
1083 /* We have permission (given by the Samba admin) to
1084 break POSIX semantics and allow a user to change
1085 the time on a file they don't own but can write to
1086 (as DOS does).
1089 /* Check if we have write access. */
1090 if (can_write_to_file(conn, smb_fname)) {
1091 /* We are allowed to become root and change the filetime. */
1092 become_root();
1093 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
1094 unbecome_root();
1097 return ret;
1100 /******************************************************************
1101 Force a "sticky" write time on a pathname. This will always be
1102 returned on all future write time queries and set on close.
1103 ******************************************************************/
1105 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1107 if (null_timespec(mtime)) {
1108 return true;
1111 if (!set_sticky_write_time(fileid, mtime)) {
1112 return false;
1115 return true;
1118 /******************************************************************
1119 Force a "sticky" write time on an fsp. This will always be
1120 returned on all future write time queries and set on close.
1121 ******************************************************************/
1123 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1125 if (null_timespec(mtime)) {
1126 return true;
1129 fsp->write_time_forced = true;
1130 TALLOC_FREE(fsp->update_write_time_event);
1132 return set_sticky_write_time_path(fsp->file_id, mtime);
1135 /******************************************************************
1136 Set a create time EA.
1137 ******************************************************************/
1139 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1140 const struct smb_filename *psmb_fname,
1141 struct timespec create_time)
1143 struct smb_filename *smb_fname;
1144 uint32_t dosmode;
1145 int ret;
1147 if (!lp_store_dos_attributes(SNUM(conn))) {
1148 return NT_STATUS_OK;
1151 smb_fname = synthetic_smb_fname(talloc_tos(), psmb_fname->base_name,
1152 NULL, &psmb_fname->st);
1154 if (smb_fname == NULL) {
1155 return NT_STATUS_NO_MEMORY;
1158 dosmode = dos_mode(conn, smb_fname);
1160 smb_fname->st.st_ex_btime = create_time;
1162 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1163 if (ret == -1) {
1164 map_nt_error_from_unix(errno);
1167 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1168 smb_fname_str_dbg(smb_fname)));
1170 return NT_STATUS_OK;
1173 /******************************************************************
1174 Return a create time.
1175 ******************************************************************/
1177 struct timespec get_create_timespec(connection_struct *conn,
1178 struct files_struct *fsp,
1179 const struct smb_filename *smb_fname)
1181 return smb_fname->st.st_ex_btime;
1184 /******************************************************************
1185 Return a change time (may look at EA in future).
1186 ******************************************************************/
1188 struct timespec get_change_timespec(connection_struct *conn,
1189 struct files_struct *fsp,
1190 const struct smb_filename *smb_fname)
1192 return smb_fname->st.st_ex_mtime;