libcli/ldap/ldap_ndr.h: fix licence/copyright
[Samba.git] / source3 / smbd / dosmode.c
blob823d44d0aaf08f64190fcf5f178e29be584d74c5
1 /*
2 Unix SMB/CIFS implementation.
3 dos mode handling functions
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) James Peach 2006
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "includes.h"
22 #include "system/filesys.h"
23 #include "librpc/gen_ndr/ndr_xattr.h"
24 #include "../libcli/security/security.h"
25 #include "smbd/smbd.h"
27 static uint32_t filter_mode_by_protocol(uint32_t mode)
29 if (get_Protocol() <= PROTOCOL_LANMAN2) {
30 DEBUG(10,("filter_mode_by_protocol: "
31 "filtering result 0x%x to 0x%x\n",
32 (unsigned int)mode,
33 (unsigned int)(mode & 0x3f) ));
34 mode &= 0x3f;
36 return mode;
39 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
41 #ifdef S_ISLNK
42 #if LINKS_READ_ONLY
43 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
44 return FILE_ATTRIBUTE_READONLY;
45 #endif
46 #endif
47 return 0;
50 /****************************************************************************
51 Change a dos mode to a unix mode.
52 Base permission for files:
53 if creating file and inheriting (i.e. parent_dir != NULL)
54 apply read/write bits from parent directory.
55 else
56 everybody gets read bit set
57 dos readonly is represented in unix by removing everyone's write bit
58 dos archive is represented in unix by the user's execute bit
59 dos system is represented in unix by the group's execute bit
60 dos hidden is represented in unix by the other's execute bit
61 if !inheriting {
62 Then apply create mask,
63 then add force bits.
65 Base permission for directories:
66 dos directory is represented in unix by unix's dir bit and the exec bit
67 if !inheriting {
68 Then apply create mask,
69 then add force bits.
71 ****************************************************************************/
73 mode_t unix_mode(connection_struct *conn, int dosmode,
74 const struct smb_filename *smb_fname,
75 const char *inherit_from_dir)
77 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
78 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
79 * inheriting. */
81 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
82 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
85 if ((inherit_from_dir != NULL) && lp_inherit_perms(SNUM(conn))) {
86 struct smb_filename *smb_fname_parent = NULL;
87 NTSTATUS status;
89 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
90 smb_fname_str_dbg(smb_fname),
91 inherit_from_dir));
93 status = create_synthetic_smb_fname(talloc_tos(),
94 inherit_from_dir, NULL,
95 NULL, &smb_fname_parent);
96 if (!NT_STATUS_IS_OK(status)) {
97 DEBUG(1,("unix_mode(%s) failed, [dir %s]: %s\n",
98 smb_fname_str_dbg(smb_fname),
99 inherit_from_dir, nt_errstr(status)));
100 return(0);
103 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
104 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
105 smb_fname_str_dbg(smb_fname),
106 inherit_from_dir, strerror(errno)));
107 TALLOC_FREE(smb_fname_parent);
108 return(0); /* *** shouldn't happen! *** */
111 /* Save for later - but explicitly remove setuid bit for safety. */
112 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
113 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
114 smb_fname_str_dbg(smb_fname), (int)dir_mode));
115 /* Clear "result" */
116 result = 0;
117 TALLOC_FREE(smb_fname_parent);
120 if (IS_DOS_DIR(dosmode)) {
121 /* We never make directories read only for the owner as under DOS a user
122 can always create a file in a read-only directory. */
123 result |= (S_IFDIR | S_IWUSR);
125 if (dir_mode) {
126 /* Inherit mode of parent directory. */
127 result |= dir_mode;
128 } else {
129 /* Provisionally add all 'x' bits */
130 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
132 /* Apply directory mask */
133 result &= lp_dir_mask(SNUM(conn));
134 /* Add in force bits */
135 result |= lp_force_dir_mode(SNUM(conn));
137 } else {
138 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
139 result |= S_IXUSR;
141 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
142 result |= S_IXGRP;
144 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
145 result |= S_IXOTH;
147 if (dir_mode) {
148 /* Inherit 666 component of parent directory mode */
149 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
150 } else {
151 /* Apply mode mask */
152 result &= lp_create_mask(SNUM(conn));
153 /* Add in force bits */
154 result |= lp_force_create_mode(SNUM(conn));
158 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
159 (int)result));
160 return(result);
163 /****************************************************************************
164 Change a unix mode to a dos mode.
165 ****************************************************************************/
167 static uint32 dos_mode_from_sbuf(connection_struct *conn,
168 const struct smb_filename *smb_fname)
170 int result = 0;
171 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
173 if (ro_opts == MAP_READONLY_YES) {
174 /* Original Samba method - map inverse of user "w" bit. */
175 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
176 result |= FILE_ATTRIBUTE_READONLY;
178 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
179 /* Check actual permissions for read-only. */
180 if (!can_write_to_file(conn, smb_fname)) {
181 result |= FILE_ATTRIBUTE_READONLY;
183 } /* Else never set the readonly bit. */
185 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
186 result |= FILE_ATTRIBUTE_ARCHIVE;
188 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
189 result |= FILE_ATTRIBUTE_SYSTEM;
191 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
192 result |= FILE_ATTRIBUTE_HIDDEN;
194 if (S_ISDIR(smb_fname->st.st_ex_mode))
195 result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
197 result |= set_link_read_only_flag(&smb_fname->st);
199 DEBUG(8,("dos_mode_from_sbuf returning "));
201 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
202 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
203 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
204 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
205 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
207 DEBUG(8,("\n"));
208 return result;
211 /****************************************************************************
212 Get DOS attributes from an EA.
213 This can also pull the create time into the stat struct inside smb_fname.
214 ****************************************************************************/
216 static bool get_ea_dos_attribute(connection_struct *conn,
217 struct smb_filename *smb_fname,
218 uint32 *pattr)
220 struct xattr_DOSATTRIB dosattrib;
221 enum ndr_err_code ndr_err;
222 DATA_BLOB blob;
223 ssize_t sizeret;
224 fstring attrstr;
225 uint32_t dosattr;
227 if (!lp_store_dos_attributes(SNUM(conn))) {
228 return False;
231 /* Don't reset pattr to zero as we may already have filename-based attributes we
232 need to preserve. */
234 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
235 SAMBA_XATTR_DOS_ATTRIB, attrstr,
236 sizeof(attrstr));
237 if (sizeret == -1) {
238 if (errno == ENOSYS
239 #if defined(ENOTSUP)
240 || errno == ENOTSUP) {
241 #else
243 #endif
244 DEBUG(1,("get_ea_dos_attribute: Cannot get attribute "
245 "from EA on file %s: Error = %s\n",
246 smb_fname_str_dbg(smb_fname),
247 strerror(errno)));
248 set_store_dos_attributes(SNUM(conn), False);
250 return False;
253 blob.data = (uint8_t *)attrstr;
254 blob.length = sizeret;
256 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib,
257 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
259 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
260 DEBUG(1,("get_ea_dos_attribute: bad ndr decode "
261 "from EA on file %s: Error = %s\n",
262 smb_fname_str_dbg(smb_fname),
263 ndr_errstr(ndr_err)));
264 return false;
267 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
268 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
270 switch (dosattrib.version) {
271 case 0xFFFF:
272 dosattr = dosattrib.info.compatinfoFFFF.attrib;
273 break;
274 case 1:
275 dosattr = dosattrib.info.info1.attrib;
276 if (!null_nttime(dosattrib.info.info1.create_time)) {
277 struct timespec create_time =
278 nt_time_to_unix_timespec(
279 &dosattrib.info.info1.create_time);
281 update_stat_ex_create_time(&smb_fname->st,
282 create_time);
284 DEBUG(10,("get_ea_dos_attribute: file %s case 1 "
285 "set btime %s\n",
286 smb_fname_str_dbg(smb_fname),
287 time_to_asc(convert_timespec_to_time_t(
288 create_time)) ));
290 break;
291 case 2:
292 dosattr = dosattrib.info.oldinfo2.attrib;
293 /* Don't know what flags to check for this case. */
294 break;
295 case 3:
296 dosattr = dosattrib.info.info3.attrib;
297 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
298 !null_nttime(dosattrib.info.info3.create_time)) {
299 struct timespec create_time =
300 nt_time_to_unix_timespec(
301 &dosattrib.info.info3.create_time);
303 update_stat_ex_create_time(&smb_fname->st,
304 create_time);
306 DEBUG(10,("get_ea_dos_attribute: file %s case 3 "
307 "set btime %s\n",
308 smb_fname_str_dbg(smb_fname),
309 time_to_asc(convert_timespec_to_time_t(
310 create_time)) ));
312 break;
313 default:
314 DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on "
315 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
316 attrstr));
317 return false;
320 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
321 dosattr |= FILE_ATTRIBUTE_DIRECTORY;
323 /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */
324 *pattr = (uint32)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE));
326 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
328 if (dosattr & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
329 if (dosattr & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
330 if (dosattr & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
331 if (dosattr & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
332 if (dosattr & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
334 DEBUG(8,("\n"));
336 return True;
339 /****************************************************************************
340 Set DOS attributes in an EA.
341 Also sets the create time.
342 ****************************************************************************/
344 static bool set_ea_dos_attribute(connection_struct *conn,
345 struct smb_filename *smb_fname,
346 uint32 dosmode)
348 struct xattr_DOSATTRIB dosattrib;
349 enum ndr_err_code ndr_err;
350 DATA_BLOB blob;
352 if (!lp_store_dos_attributes(SNUM(conn))) {
353 return False;
356 ZERO_STRUCT(dosattrib);
357 ZERO_STRUCT(blob);
359 dosattrib.version = 3;
360 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
361 XATTR_DOSINFO_CREATE_TIME;
362 dosattrib.info.info3.attrib = dosmode;
363 unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
364 smb_fname->st.st_ex_btime);
366 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
367 (unsigned int)dosmode,
368 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
369 smb_fname_str_dbg(smb_fname) ));
371 ndr_err = ndr_push_struct_blob(
372 &blob, talloc_tos(), &dosattrib,
373 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
375 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
376 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
377 ndr_errstr(ndr_err)));
378 return false;
381 if (blob.data == NULL || blob.length == 0) {
382 return false;
385 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
386 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
387 0) == -1) {
388 bool ret = false;
389 files_struct *fsp = NULL;
391 if((errno != EPERM) && (errno != EACCES)) {
392 if (errno == ENOSYS
393 #if defined(ENOTSUP)
394 || errno == ENOTSUP) {
395 #else
397 #endif
398 DEBUG(1,("set_ea_dos_attributes: Cannot set "
399 "attribute EA on file %s: Error = %s\n",
400 smb_fname_str_dbg(smb_fname),
401 strerror(errno) ));
402 set_store_dos_attributes(SNUM(conn), False);
404 return false;
407 /* We want DOS semantics, ie allow non owner with write permission to change the
408 bits on a file. Just like file_ntimes below.
411 /* Check if we have write access. */
412 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
413 return false;
416 * We need to open the file with write access whilst
417 * still in our current user context. This ensures we
418 * are not violating security in doing the setxattr.
421 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
422 &fsp)))
423 return false;
424 become_root();
425 if (SMB_VFS_FSETXATTR(fsp,
426 SAMBA_XATTR_DOS_ATTRIB, blob.data,
427 blob.length, 0) == 0) {
428 ret = true;
430 unbecome_root();
431 close_file(NULL, fsp, NORMAL_CLOSE);
432 return ret;
434 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
435 (unsigned int)dosmode,
436 smb_fname_str_dbg(smb_fname)));
437 return true;
440 /****************************************************************************
441 Change a unix mode to a dos mode for an ms dfs link.
442 ****************************************************************************/
444 uint32 dos_mode_msdfs(connection_struct *conn,
445 const struct smb_filename *smb_fname)
447 uint32 result = 0;
449 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
451 if (!VALID_STAT(smb_fname->st)) {
452 return 0;
455 /* First do any modifications that depend on the path name. */
456 /* hide files with a name starting with a . */
457 if (lp_hide_dot_files(SNUM(conn))) {
458 const char *p = strrchr_m(smb_fname->base_name, '/');
459 if (p) {
460 p++;
461 } else {
462 p = smb_fname->base_name;
465 /* Only . and .. are not hidden. */
466 if (p[0] == '.' && !((p[1] == '\0') ||
467 (p[1] == '.' && p[2] == '\0'))) {
468 result |= FILE_ATTRIBUTE_HIDDEN;
472 result |= dos_mode_from_sbuf(conn, smb_fname);
474 /* Optimization : Only call is_hidden_path if it's not already
475 hidden. */
476 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
477 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
478 result |= FILE_ATTRIBUTE_HIDDEN;
481 if (result == 0) {
482 result = FILE_ATTRIBUTE_NORMAL;
485 result = filter_mode_by_protocol(result);
487 DEBUG(8,("dos_mode_msdfs returning "));
489 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
490 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
491 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
492 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
493 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
494 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
496 DEBUG(8,("\n"));
498 return(result);
501 #ifdef HAVE_STAT_DOS_FLAGS
502 /****************************************************************************
503 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
504 ****************************************************************************/
506 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
508 uint32_t dos_stat_flags = 0;
510 if (dosmode & FILE_ATTRIBUTE_ARCHIVE)
511 dos_stat_flags |= UF_DOS_ARCHIVE;
512 if (dosmode & FILE_ATTRIBUTE_HIDDEN)
513 dos_stat_flags |= UF_DOS_HIDDEN;
514 if (dosmode & FILE_ATTRIBUTE_READONLY)
515 dos_stat_flags |= UF_DOS_RO;
516 if (dosmode & FILE_ATTRIBUTE_SYSTEM)
517 dos_stat_flags |= UF_DOS_SYSTEM;
518 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
519 dos_stat_flags |= UF_DOS_NOINDEX;
521 return dos_stat_flags;
524 /****************************************************************************
525 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
526 ****************************************************************************/
528 static bool get_stat_dos_flags(connection_struct *conn,
529 const struct smb_filename *smb_fname,
530 uint32_t *dosmode)
532 SMB_ASSERT(VALID_STAT(smb_fname->st));
533 SMB_ASSERT(dosmode);
535 if (!lp_store_dos_attributes(SNUM(conn))) {
536 return false;
539 DEBUG(5, ("Getting stat dos attributes for %s.\n",
540 smb_fname_str_dbg(smb_fname)));
542 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
543 *dosmode |= FILE_ATTRIBUTE_ARCHIVE;
544 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
545 *dosmode |= FILE_ATTRIBUTE_HIDDEN;
546 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
547 *dosmode |= FILE_ATTRIBUTE_READONLY;
548 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
549 *dosmode |= FILE_ATTRIBUTE_SYSTEM;
550 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
551 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
552 if (smb_fname->st.st_ex_flags & FILE_ATTRIBUTE_SPARSE)
553 *dosmode |= FILE_ATTRIBUTE_SPARSE;
554 if (S_ISDIR(smb_fname->st.st_ex_mode))
555 *dosmode |= FILE_ATTRIBUTE_DIRECTORY;
557 *dosmode |= set_link_read_only_flag(&smb_fname->st);
559 return true;
562 /****************************************************************************
563 Sets DOS attributes, stored in st_ex_flags of the inode.
564 ****************************************************************************/
566 static bool set_stat_dos_flags(connection_struct *conn,
567 const struct smb_filename *smb_fname,
568 uint32_t dosmode,
569 bool *attributes_changed)
571 uint32_t new_flags = 0;
572 int error = 0;
574 SMB_ASSERT(VALID_STAT(smb_fname->st));
575 SMB_ASSERT(attributes_changed);
577 *attributes_changed = false;
579 if (!lp_store_dos_attributes(SNUM(conn))) {
580 return false;
583 DEBUG(5, ("Setting stat dos attributes for %s.\n",
584 smb_fname_str_dbg(smb_fname)));
586 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
587 dos_attributes_to_stat_dos_flags(dosmode);
589 /* Return early if no flags changed. */
590 if (new_flags == smb_fname->st.st_ex_flags)
591 return true;
593 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
594 smb_fname->st.st_ex_flags));
596 /* Set new flags with chflags. */
597 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
598 if (error) {
599 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
600 "file %s! errno=%d\n", new_flags,
601 smb_fname_str_dbg(smb_fname), errno));
602 return false;
605 *attributes_changed = true;
606 return true;
608 #endif /* HAVE_STAT_DOS_FLAGS */
610 /****************************************************************************
611 Change a unix mode to a dos mode.
612 May also read the create timespec into the stat struct in smb_fname
613 if "store dos attributes" is true.
614 ****************************************************************************/
616 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
618 uint32 result = 0;
619 bool offline, used_stat_dos_flags = false;
621 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
623 if (!VALID_STAT(smb_fname->st)) {
624 return 0;
627 /* First do any modifications that depend on the path name. */
628 /* hide files with a name starting with a . */
629 if (lp_hide_dot_files(SNUM(conn))) {
630 const char *p = strrchr_m(smb_fname->base_name,'/');
631 if (p) {
632 p++;
633 } else {
634 p = smb_fname->base_name;
637 /* Only . and .. are not hidden. */
638 if (p[0] == '.' && !((p[1] == '\0') ||
639 (p[1] == '.' && p[2] == '\0'))) {
640 result |= FILE_ATTRIBUTE_HIDDEN;
644 #ifdef HAVE_STAT_DOS_FLAGS
645 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
646 #endif
647 if (!used_stat_dos_flags) {
648 /* Get the DOS attributes from an EA by preference. */
649 if (!get_ea_dos_attribute(conn, smb_fname, &result)) {
650 result |= dos_mode_from_sbuf(conn, smb_fname);
654 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st);
655 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
656 result |= FILE_ATTRIBUTE_OFFLINE;
659 /* Optimization : Only call is_hidden_path if it's not already
660 hidden. */
661 if (!(result & FILE_ATTRIBUTE_HIDDEN) &&
662 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
663 result |= FILE_ATTRIBUTE_HIDDEN;
666 if (result == 0) {
667 result = FILE_ATTRIBUTE_NORMAL;
670 result = filter_mode_by_protocol(result);
672 DEBUG(8,("dos_mode returning "));
674 if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h"));
675 if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r"));
676 if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s"));
677 if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d"));
678 if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a"));
679 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
681 DEBUG(8,("\n"));
683 return(result);
686 /*******************************************************************
687 chmod a file - but preserve some bits.
688 If "store dos attributes" is also set it will store the create time
689 from the stat struct in smb_fname (in NTTIME format) in the EA
690 attribute also.
691 ********************************************************************/
693 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
694 uint32 dosmode, const char *parent_dir, bool newfile)
696 int mask=0;
697 mode_t tmp;
698 mode_t unixmode;
699 int ret = -1, lret = -1;
700 uint32_t old_mode;
701 struct timespec new_create_timespec;
703 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
704 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
706 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
707 dosmode, smb_fname_str_dbg(smb_fname)));
709 unixmode = smb_fname->st.st_ex_mode;
711 get_acl_group_bits(conn, smb_fname->base_name,
712 &smb_fname->st.st_ex_mode);
714 if (S_ISDIR(smb_fname->st.st_ex_mode))
715 dosmode |= FILE_ATTRIBUTE_DIRECTORY;
716 else
717 dosmode &= ~FILE_ATTRIBUTE_DIRECTORY;
719 new_create_timespec = smb_fname->st.st_ex_btime;
721 old_mode = dos_mode(conn, smb_fname);
723 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
724 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
725 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname);
726 if (lret == -1) {
727 DEBUG(0, ("set_dos_mode: client has asked to "
728 "set FILE_ATTRIBUTE_OFFLINE to "
729 "%s/%s but there was an error while "
730 "setting it or it is not "
731 "supported.\n", parent_dir,
732 smb_fname_str_dbg(smb_fname)));
737 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
738 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
740 smb_fname->st.st_ex_btime = new_create_timespec;
742 #ifdef HAVE_STAT_DOS_FLAGS
744 bool attributes_changed;
746 if (set_stat_dos_flags(conn, smb_fname, dosmode,
747 &attributes_changed))
749 if (!newfile && attributes_changed) {
750 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
751 FILE_NOTIFY_CHANGE_ATTRIBUTES,
752 smb_fname->base_name);
754 smb_fname->st.st_ex_mode = unixmode;
755 return 0;
758 #endif
759 /* Store the DOS attributes in an EA by preference. */
760 if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
761 if (!newfile) {
762 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
763 FILE_NOTIFY_CHANGE_ATTRIBUTES,
764 smb_fname->base_name);
766 smb_fname->st.st_ex_mode = unixmode;
767 return 0;
770 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
772 /* preserve the s bits */
773 mask |= (S_ISUID | S_ISGID);
775 /* preserve the t bit */
776 #ifdef S_ISVTX
777 mask |= S_ISVTX;
778 #endif
780 /* possibly preserve the x bits */
781 if (!MAP_ARCHIVE(conn))
782 mask |= S_IXUSR;
783 if (!MAP_SYSTEM(conn))
784 mask |= S_IXGRP;
785 if (!MAP_HIDDEN(conn))
786 mask |= S_IXOTH;
788 unixmode |= (smb_fname->st.st_ex_mode & mask);
790 /* if we previously had any r bits set then leave them alone */
791 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
792 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
793 unixmode |= tmp;
796 /* if we previously had any w bits set then leave them alone
797 whilst adding in the new w bits, if the new mode is not rdonly */
798 if (!IS_DOS_READONLY(dosmode)) {
799 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
803 * From the chmod 2 man page:
805 * "If the calling process is not privileged, and the group of the file
806 * does not match the effective group ID of the process or one of its
807 * supplementary group IDs, the S_ISGID bit will be turned off, but
808 * this will not cause an error to be returned."
810 * Simply refuse to do the chmod in this case.
813 if (S_ISDIR(smb_fname->st.st_ex_mode) && (unixmode & S_ISGID) &&
814 geteuid() != sec_initial_uid() &&
815 !current_user_in_group(conn, smb_fname->st.st_ex_gid)) {
816 DEBUG(3,("file_set_dosmode: setgid bit cannot be "
817 "set for directory %s\n",
818 smb_fname_str_dbg(smb_fname)));
819 errno = EPERM;
820 return -1;
823 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
824 if (ret == 0) {
825 if(!newfile || (lret != -1)) {
826 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
827 FILE_NOTIFY_CHANGE_ATTRIBUTES,
828 smb_fname->base_name);
830 smb_fname->st.st_ex_mode = unixmode;
831 return 0;
834 if((errno != EPERM) && (errno != EACCES))
835 return -1;
837 if(!lp_dos_filemode(SNUM(conn)))
838 return -1;
840 /* We want DOS semantics, ie allow non owner with write permission to change the
841 bits on a file. Just like file_ntimes below.
844 /* Check if we have write access. */
845 if (CAN_WRITE(conn)) {
847 * We need to open the file with write access whilst
848 * still in our current user context. This ensures we
849 * are not violating security in doing the fchmod.
851 files_struct *fsp;
852 if (!NT_STATUS_IS_OK(open_file_fchmod(conn, smb_fname,
853 &fsp)))
854 return -1;
855 become_root();
856 ret = SMB_VFS_FCHMOD(fsp, unixmode);
857 unbecome_root();
858 close_file(NULL, fsp, NORMAL_CLOSE);
859 if (!newfile) {
860 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
861 FILE_NOTIFY_CHANGE_ATTRIBUTES,
862 smb_fname->base_name);
864 if (ret == 0) {
865 smb_fname->st.st_ex_mode = unixmode;
869 return( ret );
873 NTSTATUS file_set_sparse(connection_struct *conn,
874 files_struct *fsp,
875 bool sparse)
877 uint32_t old_dosmode;
878 uint32_t new_dosmode;
879 NTSTATUS status;
881 if (!CAN_WRITE(conn)) {
882 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
883 "on readonly share[%s]\n",
884 smb_fname_str_dbg(fsp->fsp_name),
885 sparse,
886 lp_servicename(SNUM(conn))));
887 return NT_STATUS_MEDIA_WRITE_PROTECTED;
890 if (!(fsp->access_mask & FILE_WRITE_DATA) &&
891 !(fsp->access_mask & FILE_WRITE_ATTRIBUTES)) {
892 DEBUG(9,("file_set_sparse: fname[%s] set[%u] "
893 "access_mask[0x%08X] - access denied\n",
894 smb_fname_str_dbg(fsp->fsp_name),
895 sparse,
896 fsp->access_mask));
897 return NT_STATUS_ACCESS_DENIED;
900 DEBUG(10,("file_set_sparse: setting sparse bit %u on file %s\n",
901 sparse, smb_fname_str_dbg(fsp->fsp_name)));
903 if (!lp_store_dos_attributes(SNUM(conn))) {
904 return NT_STATUS_INVALID_DEVICE_REQUEST;
907 status = vfs_stat_fsp(fsp);
908 if (!NT_STATUS_IS_OK(status)) {
909 return status;
912 old_dosmode = dos_mode(conn, fsp->fsp_name);
914 if (sparse && !(old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
915 new_dosmode = old_dosmode | FILE_ATTRIBUTE_SPARSE;
916 } else if (!sparse && (old_dosmode & FILE_ATTRIBUTE_SPARSE)) {
917 new_dosmode = old_dosmode & ~FILE_ATTRIBUTE_SPARSE;
918 } else {
919 return NT_STATUS_OK;
922 /* Store the DOS attributes in an EA. */
923 if (!set_ea_dos_attribute(conn, fsp->fsp_name,
924 new_dosmode)) {
925 if (errno == 0) {
926 errno = EIO;
928 return map_nt_error_from_unix(errno);
931 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
932 FILE_NOTIFY_CHANGE_ATTRIBUTES,
933 fsp->fsp_name->base_name);
935 fsp->is_sparse = sparse;
937 return NT_STATUS_OK;
940 /*******************************************************************
941 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
942 than POSIX.
943 *******************************************************************/
945 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
946 struct smb_file_time *ft)
948 int ret = -1;
950 errno = 0;
952 DEBUG(6, ("file_ntime: actime: %s",
953 time_to_asc(convert_timespec_to_time_t(ft->atime))));
954 DEBUG(6, ("file_ntime: modtime: %s",
955 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
956 DEBUG(6, ("file_ntime: ctime: %s",
957 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
958 DEBUG(6, ("file_ntime: createtime: %s",
959 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
961 /* Don't update the time on read-only shares */
962 /* We need this as set_filetime (which can be called on
963 close and other paths) can end up calling this function
964 without the NEED_WRITE protection. Found by :
965 Leo Weppelman <leo@wau.mis.ah.nl>
968 if (!CAN_WRITE(conn)) {
969 return 0;
972 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
973 return 0;
976 if((errno != EPERM) && (errno != EACCES)) {
977 return -1;
980 if(!lp_dos_filetimes(SNUM(conn))) {
981 return -1;
984 /* We have permission (given by the Samba admin) to
985 break POSIX semantics and allow a user to change
986 the time on a file they don't own but can write to
987 (as DOS does).
990 /* Check if we have write access. */
991 if (can_write_to_file(conn, smb_fname)) {
992 /* We are allowed to become root and change the filetime. */
993 become_root();
994 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
995 unbecome_root();
998 return ret;
1001 /******************************************************************
1002 Force a "sticky" write time on a pathname. This will always be
1003 returned on all future write time queries and set on close.
1004 ******************************************************************/
1006 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
1008 if (null_timespec(mtime)) {
1009 return true;
1012 if (!set_sticky_write_time(fileid, mtime)) {
1013 return false;
1016 return true;
1019 /******************************************************************
1020 Force a "sticky" write time on an fsp. This will always be
1021 returned on all future write time queries and set on close.
1022 ******************************************************************/
1024 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
1026 if (null_timespec(mtime)) {
1027 return true;
1030 fsp->write_time_forced = true;
1031 TALLOC_FREE(fsp->update_write_time_event);
1033 return set_sticky_write_time_path(fsp->file_id, mtime);
1036 /******************************************************************
1037 Set a create time EA.
1038 ******************************************************************/
1040 NTSTATUS set_create_timespec_ea(connection_struct *conn,
1041 const struct smb_filename *psmb_fname,
1042 struct timespec create_time)
1044 NTSTATUS status;
1045 struct smb_filename *smb_fname = NULL;
1046 uint32_t dosmode;
1047 int ret;
1049 if (!lp_store_dos_attributes(SNUM(conn))) {
1050 return NT_STATUS_OK;
1053 status = create_synthetic_smb_fname(talloc_tos(),
1054 psmb_fname->base_name,
1055 NULL, &psmb_fname->st,
1056 &smb_fname);
1058 if (!NT_STATUS_IS_OK(status)) {
1059 return status;
1062 dosmode = dos_mode(conn, smb_fname);
1064 smb_fname->st.st_ex_btime = create_time;
1066 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
1067 if (ret == -1) {
1068 map_nt_error_from_unix(errno);
1071 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
1072 smb_fname_str_dbg(smb_fname)));
1074 return NT_STATUS_OK;
1077 /******************************************************************
1078 Return a create time.
1079 ******************************************************************/
1081 struct timespec get_create_timespec(connection_struct *conn,
1082 struct files_struct *fsp,
1083 const struct smb_filename *smb_fname)
1085 return smb_fname->st.st_ex_btime;
1088 /******************************************************************
1089 Return a change time (may look at EA in future).
1090 ******************************************************************/
1092 struct timespec get_change_timespec(connection_struct *conn,
1093 struct files_struct *fsp,
1094 const struct smb_filename *smb_fname)
1096 return smb_fname->st.st_ex_mtime;