libutil: Remove obsolete signal type cast.
[Samba/nascimento.git] / source3 / smbd / dosmode.c
blob436556344b3ea648c1da8c732aa8690c7741e73f
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 "librpc/gen_ndr/ndr_xattr.h"
24 static uint32_t filter_mode_by_protocol(uint32_t mode)
26 if (get_Protocol() <= PROTOCOL_LANMAN2) {
27 DEBUG(10,("filter_mode_by_protocol: "
28 "filtering result 0x%x to 0x%x\n",
29 (unsigned int)mode,
30 (unsigned int)(mode & 0x3f) ));
31 mode &= 0x3f;
33 return mode;
36 static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
38 #if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
39 if (sbuf->st_ex_size > sbuf->st_ex_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
40 return FILE_ATTRIBUTE_SPARSE;
42 #endif
43 return 0;
46 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
48 #ifdef S_ISLNK
49 #if LINKS_READ_ONLY
50 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
51 return aRONLY;
52 #endif
53 #endif
54 return 0;
57 /****************************************************************************
58 Change a dos mode to a unix mode.
59 Base permission for files:
60 if creating file and inheriting (i.e. parent_dir != NULL)
61 apply read/write bits from parent directory.
62 else
63 everybody gets read bit set
64 dos readonly is represented in unix by removing everyone's write bit
65 dos archive is represented in unix by the user's execute bit
66 dos system is represented in unix by the group's execute bit
67 dos hidden is represented in unix by the other's execute bit
68 if !inheriting {
69 Then apply create mask,
70 then add force bits.
72 Base permission for directories:
73 dos directory is represented in unix by unix's dir bit and the exec bit
74 if !inheriting {
75 Then apply create mask,
76 then add force bits.
78 ****************************************************************************/
80 mode_t unix_mode(connection_struct *conn, int dosmode,
81 const struct smb_filename *smb_fname,
82 const char *inherit_from_dir)
84 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
85 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
86 * inheriting. */
88 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
89 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
92 if ((inherit_from_dir != NULL) && lp_inherit_perms(SNUM(conn))) {
93 struct smb_filename *smb_fname_parent = NULL;
94 NTSTATUS status;
96 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
97 smb_fname_str_dbg(smb_fname),
98 inherit_from_dir));
100 status = create_synthetic_smb_fname(talloc_tos(),
101 inherit_from_dir, NULL,
102 NULL, &smb_fname_parent);
103 if (!NT_STATUS_IS_OK(status)) {
104 DEBUG(1,("unix_mode(%s) failed, [dir %s]: %s\n",
105 smb_fname_str_dbg(smb_fname),
106 inherit_from_dir, nt_errstr(status)));
107 return(0);
110 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
111 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
112 smb_fname_str_dbg(smb_fname),
113 inherit_from_dir, strerror(errno)));
114 TALLOC_FREE(smb_fname_parent);
115 return(0); /* *** shouldn't happen! *** */
118 /* Save for later - but explicitly remove setuid bit for safety. */
119 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
120 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
121 smb_fname_str_dbg(smb_fname), (int)dir_mode));
122 /* Clear "result" */
123 result = 0;
124 TALLOC_FREE(smb_fname_parent);
127 if (IS_DOS_DIR(dosmode)) {
128 /* We never make directories read only for the owner as under DOS a user
129 can always create a file in a read-only directory. */
130 result |= (S_IFDIR | S_IWUSR);
132 if (dir_mode) {
133 /* Inherit mode of parent directory. */
134 result |= dir_mode;
135 } else {
136 /* Provisionally add all 'x' bits */
137 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
139 /* Apply directory mask */
140 result &= lp_dir_mask(SNUM(conn));
141 /* Add in force bits */
142 result |= lp_force_dir_mode(SNUM(conn));
144 } else {
145 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
146 result |= S_IXUSR;
148 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
149 result |= S_IXGRP;
151 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
152 result |= S_IXOTH;
154 if (dir_mode) {
155 /* Inherit 666 component of parent directory mode */
156 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
157 } else {
158 /* Apply mode mask */
159 result &= lp_create_mask(SNUM(conn));
160 /* Add in force bits */
161 result |= lp_force_create_mode(SNUM(conn));
165 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
166 (int)result));
167 return(result);
170 /****************************************************************************
171 Change a unix mode to a dos mode.
172 ****************************************************************************/
174 static uint32 dos_mode_from_sbuf(connection_struct *conn,
175 const struct smb_filename *smb_fname)
177 int result = 0;
178 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
180 if (ro_opts == MAP_READONLY_YES) {
181 /* Original Samba method - map inverse of user "w" bit. */
182 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
183 result |= aRONLY;
185 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
186 /* Check actual permissions for read-only. */
187 if (!can_write_to_file(conn, smb_fname)) {
188 result |= aRONLY;
190 } /* Else never set the readonly bit. */
192 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
193 result |= aARCH;
195 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
196 result |= aSYSTEM;
198 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
199 result |= aHIDDEN;
201 if (S_ISDIR(smb_fname->st.st_ex_mode))
202 result = aDIR | (result & aRONLY);
204 result |= set_sparse_flag(&smb_fname->st);
205 result |= set_link_read_only_flag(&smb_fname->st);
207 DEBUG(8,("dos_mode_from_sbuf returning "));
209 if (result & aHIDDEN) DEBUG(8, ("h"));
210 if (result & aRONLY ) DEBUG(8, ("r"));
211 if (result & aSYSTEM) DEBUG(8, ("s"));
212 if (result & aDIR ) DEBUG(8, ("d"));
213 if (result & aARCH ) DEBUG(8, ("a"));
215 DEBUG(8,("\n"));
216 return result;
219 /****************************************************************************
220 Get DOS attributes from an EA.
221 This can also pull the create time into the stat struct inside smb_fname.
222 ****************************************************************************/
224 static bool get_ea_dos_attribute(connection_struct *conn,
225 struct smb_filename *smb_fname,
226 uint32 *pattr)
228 struct xattr_DOSATTRIB dosattrib;
229 enum ndr_err_code ndr_err;
230 DATA_BLOB blob;
231 ssize_t sizeret;
232 fstring attrstr;
233 uint32_t dosattr;
235 if (!lp_store_dos_attributes(SNUM(conn))) {
236 return False;
239 /* Don't reset pattr to zero as we may already have filename-based attributes we
240 need to preserve. */
242 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
243 SAMBA_XATTR_DOS_ATTRIB, attrstr,
244 sizeof(attrstr));
245 if (sizeret == -1) {
246 if (errno == ENOSYS
247 #if defined(ENOTSUP)
248 || errno == ENOTSUP) {
249 #else
251 #endif
252 DEBUG(1,("get_ea_dos_attributes: Cannot get attribute "
253 "from EA on file %s: Error = %s\n",
254 smb_fname_str_dbg(smb_fname),
255 strerror(errno)));
256 set_store_dos_attributes(SNUM(conn), False);
258 return False;
261 blob.data = (uint8_t *)attrstr;
262 blob.length = sizeret;
264 ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), NULL, &dosattrib,
265 (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB);
267 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
268 DEBUG(1,("get_ea_dos_attributes: bad ndr decode "
269 "from EA on file %s: Error = %s\n",
270 smb_fname_str_dbg(smb_fname),
271 ndr_errstr(ndr_err)));
272 return false;
275 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
276 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
278 switch (dosattrib.version) {
279 case 0xFFFF:
280 dosattr = dosattrib.info.compatinfoFFFF.attrib;
281 break;
282 case 1:
283 dosattr = dosattrib.info.info1.attrib;
284 if (!null_nttime(dosattrib.info.info1.create_time)) {
285 struct timespec create_time =
286 nt_time_to_unix_timespec(
287 &dosattrib.info.info1.create_time);
289 update_stat_ex_create_time(&smb_fname->st,
290 create_time);
292 DEBUG(10,("get_ea_dos_attributes: file %s case 1 "
293 "set btime %s\n",
294 smb_fname_str_dbg(smb_fname),
295 time_to_asc(convert_timespec_to_time_t(
296 create_time)) ));
298 break;
299 case 2:
300 dosattr = dosattrib.info.oldinfo2.attrib;
301 /* Don't know what flags to check for this case. */
302 break;
303 case 3:
304 dosattr = dosattrib.info.info3.attrib;
305 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
306 !null_nttime(dosattrib.info.info3.create_time)) {
307 struct timespec create_time =
308 nt_time_to_unix_timespec(
309 &dosattrib.info.info3.create_time);
311 update_stat_ex_create_time(&smb_fname->st,
312 create_time);
314 DEBUG(10,("get_ea_dos_attributes: file %s case 3 "
315 "set btime %s\n",
316 smb_fname_str_dbg(smb_fname),
317 time_to_asc(convert_timespec_to_time_t(
318 create_time)) ));
320 break;
321 default:
322 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on "
323 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
324 attrstr));
325 return false;
328 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
329 dosattr |= aDIR;
331 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
333 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
335 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
336 if (dosattr & aRONLY ) DEBUG(8, ("r"));
337 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
338 if (dosattr & aDIR ) DEBUG(8, ("d"));
339 if (dosattr & aARCH ) DEBUG(8, ("a"));
341 DEBUG(8,("\n"));
343 return True;
346 /****************************************************************************
347 Set DOS attributes in an EA.
348 Also sets the create time.
349 ****************************************************************************/
351 static bool set_ea_dos_attribute(connection_struct *conn,
352 struct smb_filename *smb_fname,
353 uint32 dosmode)
355 struct xattr_DOSATTRIB dosattrib;
356 enum ndr_err_code ndr_err;
357 DATA_BLOB blob;
358 files_struct *fsp = NULL;
359 bool ret = false;
361 if (!lp_store_dos_attributes(SNUM(conn))) {
362 return False;
365 ZERO_STRUCT(dosattrib);
366 ZERO_STRUCT(blob);
368 dosattrib.version = 3;
369 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
370 XATTR_DOSINFO_CREATE_TIME;
371 dosattrib.info.info3.attrib = dosmode;
372 unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
373 smb_fname->st.st_ex_btime);
375 DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n",
376 (unsigned int)dosmode,
377 time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)),
378 smb_fname_str_dbg(smb_fname) ));
380 ndr_err = ndr_push_struct_blob(
381 &blob, talloc_tos(), NULL, &dosattrib,
382 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
384 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
385 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
386 ndr_errstr(ndr_err)));
387 return false;
390 if (blob.data == NULL || blob.length == 0) {
391 return false;
394 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
395 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
396 0) == -1) {
397 if((errno != EPERM) && (errno != EACCES)) {
398 if (errno == ENOSYS
399 #if defined(ENOTSUP)
400 || errno == ENOTSUP) {
401 #else
403 #endif
404 DEBUG(1,("set_ea_dos_attributes: Cannot set "
405 "attribute EA on file %s: Error = %s\n",
406 smb_fname_str_dbg(smb_fname),
407 strerror(errno) ));
408 set_store_dos_attributes(SNUM(conn), False);
410 return false;
413 /* We want DOS semantics, ie allow non owner with write permission to change the
414 bits on a file. Just like file_ntimes below.
417 /* Check if we have write access. */
418 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
419 return false;
422 * We need to open the file with write access whilst
423 * still in our current user context. This ensures we
424 * are not violating security in doing the setxattr.
427 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname,
428 &fsp)))
429 return ret;
430 become_root();
431 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
432 SAMBA_XATTR_DOS_ATTRIB, blob.data,
433 blob.length, 0) == 0) {
434 ret = true;
436 unbecome_root();
437 close_file_fchmod(NULL, fsp);
438 return ret;
440 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
441 (unsigned int)dosmode,
442 smb_fname_str_dbg(smb_fname)));
443 return true;
446 /****************************************************************************
447 Change a unix mode to a dos mode for an ms dfs link.
448 ****************************************************************************/
450 uint32 dos_mode_msdfs(connection_struct *conn,
451 const struct smb_filename *smb_fname)
453 uint32 result = 0;
455 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
457 if (!VALID_STAT(smb_fname->st)) {
458 return 0;
461 /* First do any modifications that depend on the path name. */
462 /* hide files with a name starting with a . */
463 if (lp_hide_dot_files(SNUM(conn))) {
464 const char *p = strrchr_m(smb_fname->base_name, '/');
465 if (p) {
466 p++;
467 } else {
468 p = smb_fname->base_name;
471 /* Only . and .. are not hidden. */
472 if (p[0] == '.' && !((p[1] == '\0') ||
473 (p[1] == '.' && p[2] == '\0'))) {
474 result |= aHIDDEN;
478 result |= dos_mode_from_sbuf(conn, smb_fname);
480 /* Optimization : Only call is_hidden_path if it's not already
481 hidden. */
482 if (!(result & aHIDDEN) &&
483 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
484 result |= aHIDDEN;
487 if (result == 0) {
488 result = FILE_ATTRIBUTE_NORMAL;
491 result = filter_mode_by_protocol(result);
493 DEBUG(8,("dos_mode_msdfs returning "));
495 if (result & aHIDDEN) DEBUG(8, ("h"));
496 if (result & aRONLY ) DEBUG(8, ("r"));
497 if (result & aSYSTEM) DEBUG(8, ("s"));
498 if (result & aDIR ) DEBUG(8, ("d"));
499 if (result & aARCH ) DEBUG(8, ("a"));
500 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
502 DEBUG(8,("\n"));
504 return(result);
507 #ifdef HAVE_STAT_DOS_FLAGS
508 /****************************************************************************
509 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
510 ****************************************************************************/
512 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
514 uint32_t dos_stat_flags = 0;
516 if (dosmode & aARCH)
517 dos_stat_flags |= UF_DOS_ARCHIVE;
518 if (dosmode & aHIDDEN)
519 dos_stat_flags |= UF_DOS_HIDDEN;
520 if (dosmode & aRONLY)
521 dos_stat_flags |= UF_DOS_RO;
522 if (dosmode & aSYSTEM)
523 dos_stat_flags |= UF_DOS_SYSTEM;
524 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
525 dos_stat_flags |= UF_DOS_NOINDEX;
527 return dos_stat_flags;
530 /****************************************************************************
531 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
532 ****************************************************************************/
534 static bool get_stat_dos_flags(connection_struct *conn,
535 const struct smb_filename *smb_fname,
536 uint32_t *dosmode)
538 SMB_ASSERT(VALID_STAT(smb_fname->st));
539 SMB_ASSERT(dosmode);
541 if (!lp_store_dos_attributes(SNUM(conn))) {
542 return false;
545 DEBUG(5, ("Getting stat dos attributes for %s.\n",
546 smb_fname_str_dbg(smb_fname)));
548 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
549 *dosmode |= aARCH;
550 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
551 *dosmode |= aHIDDEN;
552 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
553 *dosmode |= aRONLY;
554 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
555 *dosmode |= aSYSTEM;
556 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
557 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
558 if (S_ISDIR(smb_fname->st.st_ex_mode))
559 *dosmode |= aDIR;
561 *dosmode |= set_sparse_flag(&smb_fname->st);
562 *dosmode |= set_link_read_only_flag(&smb_fname->st);
564 return true;
567 /****************************************************************************
568 Sets DOS attributes, stored in st_ex_flags of the inode.
569 ****************************************************************************/
571 static bool set_stat_dos_flags(connection_struct *conn,
572 const struct smb_filename *smb_fname,
573 uint32_t dosmode,
574 bool *attributes_changed)
576 uint32_t new_flags = 0;
577 int error = 0;
579 SMB_ASSERT(VALID_STAT(smb_fname->st));
580 SMB_ASSERT(attributes_changed);
582 *attributes_changed = false;
584 if (!lp_store_dos_attributes(SNUM(conn))) {
585 return false;
588 DEBUG(5, ("Setting stat dos attributes for %s.\n",
589 smb_fname_str_dbg(smb_fname)));
591 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
592 dos_attributes_to_stat_dos_flags(dosmode);
594 /* Return early if no flags changed. */
595 if (new_flags == smb_fname->st.st_ex_flags)
596 return true;
598 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
599 smb_fname->st.st_ex_flags));
601 /* Set new flags with chflags. */
602 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
603 if (error) {
604 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
605 "file %s! errno=%d\n", new_flags,
606 smb_fname_str_dbg(smb_fname), errno));
607 return false;
610 *attributes_changed = true;
611 return true;
613 #endif /* HAVE_STAT_DOS_FLAGS */
615 /****************************************************************************
616 Change a unix mode to a dos mode.
617 May also read the create timespec into the stat struct in smb_fname
618 if "store dos attributes" is true.
619 ****************************************************************************/
621 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
623 uint32 result = 0;
624 bool offline, used_stat_dos_flags = false;
626 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
628 if (!VALID_STAT(smb_fname->st)) {
629 return 0;
632 /* First do any modifications that depend on the path name. */
633 /* hide files with a name starting with a . */
634 if (lp_hide_dot_files(SNUM(conn))) {
635 const char *p = strrchr_m(smb_fname->base_name,'/');
636 if (p) {
637 p++;
638 } else {
639 p = smb_fname->base_name;
642 /* Only . and .. are not hidden. */
643 if (p[0] == '.' && !((p[1] == '\0') ||
644 (p[1] == '.' && p[2] == '\0'))) {
645 result |= aHIDDEN;
649 #ifdef HAVE_STAT_DOS_FLAGS
650 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
651 #endif
652 if (!used_stat_dos_flags) {
653 /* Get the DOS attributes from an EA by preference. */
654 if (get_ea_dos_attribute(conn, smb_fname, &result)) {
655 result |= set_sparse_flag(&smb_fname->st);
656 } else {
657 result |= dos_mode_from_sbuf(conn, smb_fname);
661 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname->base_name, &smb_fname->st);
662 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
663 result |= FILE_ATTRIBUTE_OFFLINE;
666 /* Optimization : Only call is_hidden_path if it's not already
667 hidden. */
668 if (!(result & aHIDDEN) &&
669 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
670 result |= aHIDDEN;
673 if (result == 0) {
674 result = FILE_ATTRIBUTE_NORMAL;
677 result = filter_mode_by_protocol(result);
679 DEBUG(8,("dos_mode returning "));
681 if (result & aHIDDEN) DEBUG(8, ("h"));
682 if (result & aRONLY ) DEBUG(8, ("r"));
683 if (result & aSYSTEM) DEBUG(8, ("s"));
684 if (result & aDIR ) DEBUG(8, ("d"));
685 if (result & aARCH ) DEBUG(8, ("a"));
686 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
688 DEBUG(8,("\n"));
690 return(result);
693 /*******************************************************************
694 chmod a file - but preserve some bits.
695 If "store dos attributes" is also set it will store the create time
696 from the stat struct in smb_fname (in NTTIME format) in the EA
697 attribute also.
698 ********************************************************************/
700 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
701 uint32 dosmode, const char *parent_dir, bool newfile)
703 int mask=0;
704 mode_t tmp;
705 mode_t unixmode;
706 int ret = -1, lret = -1;
707 uint32_t old_mode;
708 struct timespec new_create_timespec;
710 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
711 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
713 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
714 dosmode, smb_fname_str_dbg(smb_fname)));
716 unixmode = smb_fname->st.st_ex_mode;
718 get_acl_group_bits(conn, smb_fname->base_name,
719 &smb_fname->st.st_ex_mode);
721 if (S_ISDIR(smb_fname->st.st_ex_mode))
722 dosmode |= aDIR;
723 else
724 dosmode &= ~aDIR;
726 new_create_timespec = smb_fname->st.st_ex_btime;
728 old_mode = dos_mode(conn, smb_fname);
730 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
731 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
732 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname->base_name);
733 if (lret == -1) {
734 DEBUG(0, ("set_dos_mode: client has asked to "
735 "set FILE_ATTRIBUTE_OFFLINE to "
736 "%s/%s but there was an error while "
737 "setting it or it is not "
738 "supported.\n", parent_dir,
739 smb_fname_str_dbg(smb_fname)));
744 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
745 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
747 smb_fname->st.st_ex_btime = new_create_timespec;
749 #ifdef HAVE_STAT_DOS_FLAGS
751 bool attributes_changed;
753 if (set_stat_dos_flags(conn, smb_fname, dosmode,
754 &attributes_changed))
756 if (!newfile && attributes_changed) {
757 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
758 FILE_NOTIFY_CHANGE_ATTRIBUTES,
759 smb_fname->base_name);
761 smb_fname->st.st_ex_mode = unixmode;
762 return 0;
765 #endif
766 /* Store the DOS attributes in an EA by preference. */
767 if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
768 if (!newfile) {
769 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
770 FILE_NOTIFY_CHANGE_ATTRIBUTES,
771 smb_fname->base_name);
773 smb_fname->st.st_ex_mode = unixmode;
774 return 0;
777 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
779 /* preserve the s bits */
780 mask |= (S_ISUID | S_ISGID);
782 /* preserve the t bit */
783 #ifdef S_ISVTX
784 mask |= S_ISVTX;
785 #endif
787 /* possibly preserve the x bits */
788 if (!MAP_ARCHIVE(conn))
789 mask |= S_IXUSR;
790 if (!MAP_SYSTEM(conn))
791 mask |= S_IXGRP;
792 if (!MAP_HIDDEN(conn))
793 mask |= S_IXOTH;
795 unixmode |= (smb_fname->st.st_ex_mode & mask);
797 /* if we previously had any r bits set then leave them alone */
798 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
799 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
800 unixmode |= tmp;
803 /* if we previously had any w bits set then leave them alone
804 whilst adding in the new w bits, if the new mode is not rdonly */
805 if (!IS_DOS_READONLY(dosmode)) {
806 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
809 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
810 if (ret == 0) {
811 if(!newfile || (lret != -1)) {
812 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
813 FILE_NOTIFY_CHANGE_ATTRIBUTES,
814 smb_fname->base_name);
816 smb_fname->st.st_ex_mode = unixmode;
817 return 0;
820 if((errno != EPERM) && (errno != EACCES))
821 return -1;
823 if(!lp_dos_filemode(SNUM(conn)))
824 return -1;
826 /* We want DOS semantics, ie allow non owner with write permission to change the
827 bits on a file. Just like file_ntimes below.
830 /* Check if we have write access. */
831 if (CAN_WRITE(conn)) {
833 * We need to open the file with write access whilst
834 * still in our current user context. This ensures we
835 * are not violating security in doing the fchmod.
836 * This file open does *not* break any oplocks we are
837 * holding. We need to review this.... may need to
838 * break batch oplocks open by others. JRA.
840 files_struct *fsp;
841 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname,
842 &fsp)))
843 return -1;
844 become_root();
845 ret = SMB_VFS_FCHMOD(fsp, unixmode);
846 unbecome_root();
847 close_file_fchmod(NULL, fsp);
848 if (!newfile) {
849 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
850 FILE_NOTIFY_CHANGE_ATTRIBUTES,
851 smb_fname->base_name);
853 if (ret == 0) {
854 smb_fname->st.st_ex_mode = unixmode;
858 return( ret );
861 /*******************************************************************
862 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
863 than POSIX.
864 *******************************************************************/
866 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
867 struct smb_file_time *ft)
869 int ret = -1;
871 errno = 0;
873 DEBUG(6, ("file_ntime: actime: %s",
874 time_to_asc(convert_timespec_to_time_t(ft->atime))));
875 DEBUG(6, ("file_ntime: modtime: %s",
876 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
877 DEBUG(6, ("file_ntime: ctime: %s",
878 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
879 DEBUG(6, ("file_ntime: createtime: %s",
880 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
882 /* Don't update the time on read-only shares */
883 /* We need this as set_filetime (which can be called on
884 close and other paths) can end up calling this function
885 without the NEED_WRITE protection. Found by :
886 Leo Weppelman <leo@wau.mis.ah.nl>
889 if (!CAN_WRITE(conn)) {
890 return 0;
893 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
894 return 0;
897 if((errno != EPERM) && (errno != EACCES)) {
898 return -1;
901 if(!lp_dos_filetimes(SNUM(conn))) {
902 return -1;
905 /* We have permission (given by the Samba admin) to
906 break POSIX semantics and allow a user to change
907 the time on a file they don't own but can write to
908 (as DOS does).
911 /* Check if we have write access. */
912 if (can_write_to_file(conn, smb_fname)) {
913 /* We are allowed to become root and change the filetime. */
914 become_root();
915 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
916 unbecome_root();
919 return ret;
922 /******************************************************************
923 Force a "sticky" write time on a pathname. This will always be
924 returned on all future write time queries and set on close.
925 ******************************************************************/
927 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
929 if (null_timespec(mtime)) {
930 return true;
933 if (!set_sticky_write_time(fileid, mtime)) {
934 return false;
937 return true;
940 /******************************************************************
941 Force a "sticky" write time on an fsp. This will always be
942 returned on all future write time queries and set on close.
943 ******************************************************************/
945 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
947 if (null_timespec(mtime)) {
948 return true;
951 fsp->write_time_forced = true;
952 TALLOC_FREE(fsp->update_write_time_event);
954 return set_sticky_write_time_path(fsp->file_id, mtime);
957 /******************************************************************
958 Set a create time EA.
959 ******************************************************************/
961 NTSTATUS set_create_timespec_ea(connection_struct *conn,
962 const struct smb_filename *psmb_fname,
963 struct timespec create_time)
965 NTSTATUS status;
966 struct smb_filename *smb_fname = NULL;
967 uint32_t dosmode;
968 int ret;
970 if (!lp_store_dos_attributes(SNUM(conn))) {
971 return NT_STATUS_OK;
974 status = create_synthetic_smb_fname(talloc_tos(),
975 psmb_fname->base_name,
976 NULL, &psmb_fname->st,
977 &smb_fname);
979 if (!NT_STATUS_IS_OK(status)) {
980 return status;
983 dosmode = dos_mode(conn, smb_fname);
985 smb_fname->st.st_ex_btime = create_time;
987 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
988 if (ret == -1) {
989 map_nt_error_from_unix(errno);
992 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
993 smb_fname_str_dbg(smb_fname)));
995 return NT_STATUS_OK;
998 /******************************************************************
999 Return a create time.
1000 ******************************************************************/
1002 struct timespec get_create_timespec(connection_struct *conn,
1003 struct files_struct *fsp,
1004 const struct smb_filename *smb_fname)
1006 return smb_fname->st.st_ex_btime;
1009 /******************************************************************
1010 Return a change time (may look at EA in future).
1011 ******************************************************************/
1013 struct timespec get_change_timespec(connection_struct *conn,
1014 struct files_struct *fsp,
1015 const struct smb_filename *smb_fname)
1017 return smb_fname->st.st_ex_mtime;