s3: remove unused Variable
[Samba/ekacnet.git] / source3 / smbd / dosmode.c
blobbc39e02285718259a033b27425646d906eeb91b8
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 DATA_BLOB blob;
230 ssize_t sizeret;
231 fstring attrstr;
232 uint32_t dosattr;
234 if (!lp_store_dos_attributes(SNUM(conn))) {
235 return False;
238 /* Don't reset pattr to zero as we may already have filename-based attributes we
239 need to preserve. */
241 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
242 SAMBA_XATTR_DOS_ATTRIB, attrstr,
243 sizeof(attrstr));
244 if (sizeret == -1) {
245 if (errno == ENOSYS
246 #if defined(ENOTSUP)
247 || errno == ENOTSUP) {
248 #else
250 #endif
251 DEBUG(1,("get_ea_dos_attributes: Cannot get attribute "
252 "from EA on file %s: Error = %s\n",
253 smb_fname_str_dbg(smb_fname),
254 strerror(errno)));
255 set_store_dos_attributes(SNUM(conn), False);
257 return False;
260 blob.data = (uint8_t *)attrstr;
261 blob.length = sizeret;
263 DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n",
264 smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex));
266 switch (dosattrib.version) {
267 case 0xFFFF:
268 dosattr = dosattrib.info.compatinfoFFFF.attrib;
269 break;
270 case 1:
271 dosattr = dosattrib.info.info1.attrib;
272 if (!null_nttime(dosattrib.info.info1.create_time)) {
273 struct timespec create_time =
274 nt_time_to_unix_timespec(
275 &dosattrib.info.info1.create_time);
277 update_stat_ex_create_time(&smb_fname->st,
278 create_time);
280 DEBUG(10,("get_ea_dos_attributes: file %s case 1 "
281 "set btime %s\n",
282 smb_fname_str_dbg(smb_fname),
283 time_to_asc(convert_timespec_to_time_t(
284 create_time)) ));
286 break;
287 case 2:
288 dosattr = dosattrib.info.oldinfo2.attrib;
289 /* Don't know what flags to check for this case. */
290 break;
291 case 3:
292 dosattr = dosattrib.info.info3.attrib;
293 if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) &&
294 !null_nttime(dosattrib.info.info3.create_time)) {
295 struct timespec create_time =
296 nt_time_to_unix_timespec(
297 &dosattrib.info.info3.create_time);
299 update_stat_ex_create_time(&smb_fname->st,
300 create_time);
302 DEBUG(10,("get_ea_dos_attributes: file %s case 3 "
303 "set btime %s\n",
304 smb_fname_str_dbg(smb_fname),
305 time_to_asc(convert_timespec_to_time_t(
306 create_time)) ));
308 break;
309 default:
310 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on "
311 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
312 attrstr));
313 return false;
316 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
317 dosattr |= aDIR;
319 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
321 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
323 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
324 if (dosattr & aRONLY ) DEBUG(8, ("r"));
325 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
326 if (dosattr & aDIR ) DEBUG(8, ("d"));
327 if (dosattr & aARCH ) DEBUG(8, ("a"));
329 DEBUG(8,("\n"));
331 return True;
334 /****************************************************************************
335 Set DOS attributes in an EA.
336 Also sets the create time.
337 ****************************************************************************/
339 static bool set_ea_dos_attribute(connection_struct *conn,
340 struct smb_filename *smb_fname,
341 uint32 dosmode)
343 struct xattr_DOSATTRIB dosattrib;
344 enum ndr_err_code ndr_err;
345 DATA_BLOB blob;
346 files_struct *fsp = NULL;
347 bool ret = false;
349 if (!lp_store_dos_attributes(SNUM(conn))) {
350 return False;
353 ZERO_STRUCT(dosattrib);
354 ZERO_STRUCT(blob);
356 dosattrib.version = 3;
357 dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB|
358 XATTR_DOSINFO_CREATE_TIME;
359 dosattrib.info.info3.attrib = dosmode;
360 unix_timespec_to_nt_time(&dosattrib.info.info3.create_time,
361 smb_fname->st.st_ex_btime);
363 ndr_err = ndr_push_struct_blob(
364 &blob, talloc_tos(), NULL, &dosattrib,
365 (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB);
367 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
368 DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n",
369 ndr_errstr(ndr_err)));
370 return false;
373 if (blob.data == NULL || blob.length == 0) {
374 return false;
377 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
378 SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length,
379 0) == -1) {
380 if((errno != EPERM) && (errno != EACCES)) {
381 if (errno == ENOSYS
382 #if defined(ENOTSUP)
383 || errno == ENOTSUP) {
384 #else
386 #endif
387 DEBUG(1,("set_ea_dos_attributes: Cannot set "
388 "attribute EA on file %s: Error = %s\n",
389 smb_fname_str_dbg(smb_fname),
390 strerror(errno) ));
391 set_store_dos_attributes(SNUM(conn), False);
393 return false;
396 /* We want DOS semantics, ie allow non owner with write permission to change the
397 bits on a file. Just like file_ntimes below.
400 /* Check if we have write access. */
401 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
402 return false;
405 * We need to open the file with write access whilst
406 * still in our current user context. This ensures we
407 * are not violating security in doing the setxattr.
410 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname,
411 &fsp)))
412 return ret;
413 become_root();
414 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
415 SAMBA_XATTR_DOS_ATTRIB, blob.data,
416 blob.length, 0) == 0) {
417 ret = true;
419 unbecome_root();
420 close_file_fchmod(NULL, fsp);
421 return ret;
423 DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n",
424 (unsigned int)dosmode,
425 smb_fname_str_dbg(smb_fname)));
426 return true;
429 /****************************************************************************
430 Change a unix mode to a dos mode for an ms dfs link.
431 ****************************************************************************/
433 uint32 dos_mode_msdfs(connection_struct *conn,
434 const struct smb_filename *smb_fname)
436 uint32 result = 0;
438 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
440 if (!VALID_STAT(smb_fname->st)) {
441 return 0;
444 /* First do any modifications that depend on the path name. */
445 /* hide files with a name starting with a . */
446 if (lp_hide_dot_files(SNUM(conn))) {
447 const char *p = strrchr_m(smb_fname->base_name, '/');
448 if (p) {
449 p++;
450 } else {
451 p = smb_fname->base_name;
454 /* Only . and .. are not hidden. */
455 if (p[0] == '.' && !((p[1] == '\0') ||
456 (p[1] == '.' && p[2] == '\0'))) {
457 result |= aHIDDEN;
461 result |= dos_mode_from_sbuf(conn, smb_fname);
463 /* Optimization : Only call is_hidden_path if it's not already
464 hidden. */
465 if (!(result & aHIDDEN) &&
466 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
467 result |= aHIDDEN;
470 if (result == 0) {
471 result = FILE_ATTRIBUTE_NORMAL;
474 result = filter_mode_by_protocol(result);
476 DEBUG(8,("dos_mode_msdfs returning "));
478 if (result & aHIDDEN) DEBUG(8, ("h"));
479 if (result & aRONLY ) DEBUG(8, ("r"));
480 if (result & aSYSTEM) DEBUG(8, ("s"));
481 if (result & aDIR ) DEBUG(8, ("d"));
482 if (result & aARCH ) DEBUG(8, ("a"));
483 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
485 DEBUG(8,("\n"));
487 return(result);
490 #ifdef HAVE_STAT_DOS_FLAGS
491 /****************************************************************************
492 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
493 ****************************************************************************/
495 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
497 uint32_t dos_stat_flags = 0;
499 if (dosmode & aARCH)
500 dos_stat_flags |= UF_DOS_ARCHIVE;
501 if (dosmode & aHIDDEN)
502 dos_stat_flags |= UF_DOS_HIDDEN;
503 if (dosmode & aRONLY)
504 dos_stat_flags |= UF_DOS_RO;
505 if (dosmode & aSYSTEM)
506 dos_stat_flags |= UF_DOS_SYSTEM;
507 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
508 dos_stat_flags |= UF_DOS_NOINDEX;
510 return dos_stat_flags;
513 /****************************************************************************
514 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
515 ****************************************************************************/
517 static bool get_stat_dos_flags(connection_struct *conn,
518 const struct smb_filename *smb_fname,
519 uint32_t *dosmode)
521 SMB_ASSERT(VALID_STAT(smb_fname->st));
522 SMB_ASSERT(dosmode);
524 if (!lp_store_dos_attributes(SNUM(conn))) {
525 return false;
528 DEBUG(5, ("Getting stat dos attributes for %s.\n",
529 smb_fname_str_dbg(smb_fname)));
531 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
532 *dosmode |= aARCH;
533 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
534 *dosmode |= aHIDDEN;
535 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
536 *dosmode |= aRONLY;
537 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
538 *dosmode |= aSYSTEM;
539 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
540 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
541 if (S_ISDIR(smb_fname->st.st_ex_mode))
542 *dosmode |= aDIR;
544 *dosmode |= set_sparse_flag(&smb_fname->st);
545 *dosmode |= set_link_read_only_flag(&smb_fname->st);
547 return true;
550 /****************************************************************************
551 Sets DOS attributes, stored in st_ex_flags of the inode.
552 ****************************************************************************/
554 static bool set_stat_dos_flags(connection_struct *conn,
555 const struct smb_filename *smb_fname,
556 uint32_t dosmode,
557 bool *attributes_changed)
559 uint32_t new_flags = 0;
560 int error = 0;
562 SMB_ASSERT(VALID_STAT(smb_fname->st));
563 SMB_ASSERT(attributes_changed);
565 *attributes_changed = false;
567 if (!lp_store_dos_attributes(SNUM(conn))) {
568 return false;
571 DEBUG(5, ("Setting stat dos attributes for %s.\n",
572 smb_fname_str_dbg(smb_fname)));
574 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
575 dos_attributes_to_stat_dos_flags(dosmode);
577 /* Return early if no flags changed. */
578 if (new_flags == smb_fname->st.st_ex_flags)
579 return true;
581 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
582 smb_fname->st.st_ex_flags));
584 /* Set new flags with chflags. */
585 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
586 if (error) {
587 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
588 "file %s! errno=%d\n", new_flags,
589 smb_fname_str_dbg(smb_fname), errno));
590 return false;
593 *attributes_changed = true;
594 return true;
596 #endif /* HAVE_STAT_DOS_FLAGS */
598 /****************************************************************************
599 Change a unix mode to a dos mode.
600 May also read the create timespec into the stat struct in smb_fname
601 if "store dos attributes" is true.
602 ****************************************************************************/
604 uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname)
606 uint32 result = 0;
607 bool offline, used_stat_dos_flags = false;
609 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
611 if (!VALID_STAT(smb_fname->st)) {
612 return 0;
615 /* First do any modifications that depend on the path name. */
616 /* hide files with a name starting with a . */
617 if (lp_hide_dot_files(SNUM(conn))) {
618 const char *p = strrchr_m(smb_fname->base_name,'/');
619 if (p) {
620 p++;
621 } else {
622 p = smb_fname->base_name;
625 /* Only . and .. are not hidden. */
626 if (p[0] == '.' && !((p[1] == '\0') ||
627 (p[1] == '.' && p[2] == '\0'))) {
628 result |= aHIDDEN;
632 #ifdef HAVE_STAT_DOS_FLAGS
633 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
634 #endif
635 if (!used_stat_dos_flags) {
636 /* Get the DOS attributes from an EA by preference. */
637 if (get_ea_dos_attribute(conn, smb_fname, &result)) {
638 result |= set_sparse_flag(&smb_fname->st);
639 } else {
640 result |= dos_mode_from_sbuf(conn, smb_fname);
644 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname->base_name, &smb_fname->st);
645 if (S_ISREG(smb_fname->st.st_ex_mode) && offline) {
646 result |= FILE_ATTRIBUTE_OFFLINE;
649 /* Optimization : Only call is_hidden_path if it's not already
650 hidden. */
651 if (!(result & aHIDDEN) &&
652 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
653 result |= aHIDDEN;
656 if (result == 0) {
657 result = FILE_ATTRIBUTE_NORMAL;
660 result = filter_mode_by_protocol(result);
662 DEBUG(8,("dos_mode returning "));
664 if (result & aHIDDEN) DEBUG(8, ("h"));
665 if (result & aRONLY ) DEBUG(8, ("r"));
666 if (result & aSYSTEM) DEBUG(8, ("s"));
667 if (result & aDIR ) DEBUG(8, ("d"));
668 if (result & aARCH ) DEBUG(8, ("a"));
669 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
671 DEBUG(8,("\n"));
673 return(result);
676 /*******************************************************************
677 chmod a file - but preserve some bits.
678 If "store dos attributes" is also set it will store the create time
679 from the stat struct in smb_fname (in NTTIME format) in the EA
680 attribute also.
681 ********************************************************************/
683 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
684 uint32 dosmode, const char *parent_dir, bool newfile)
686 int mask=0;
687 mode_t tmp;
688 mode_t unixmode;
689 int ret = -1, lret = -1;
690 uint32_t old_mode;
691 struct timespec new_create_timespec;
693 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
694 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
696 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
697 dosmode, smb_fname_str_dbg(smb_fname)));
699 unixmode = smb_fname->st.st_ex_mode;
701 get_acl_group_bits(conn, smb_fname->base_name,
702 &smb_fname->st.st_ex_mode);
704 if (S_ISDIR(smb_fname->st.st_ex_mode))
705 dosmode |= aDIR;
706 else
707 dosmode &= ~aDIR;
709 new_create_timespec = smb_fname->st.st_ex_btime;
711 old_mode = dos_mode(conn, smb_fname);
713 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
714 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
715 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname->base_name);
716 if (lret == -1) {
717 DEBUG(0, ("set_dos_mode: client has asked to "
718 "set FILE_ATTRIBUTE_OFFLINE to "
719 "%s/%s but there was an error while "
720 "setting it or it is not "
721 "supported.\n", parent_dir,
722 smb_fname_str_dbg(smb_fname)));
727 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
728 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
730 if (old_mode == dosmode &&
731 (timespec_compare(&new_create_timespec,
732 &smb_fname->st.st_ex_btime) == 0)) {
733 smb_fname->st.st_ex_mode = unixmode;
734 return(0);
737 smb_fname->st.st_ex_btime = new_create_timespec;
739 #ifdef HAVE_STAT_DOS_FLAGS
741 bool attributes_changed;
743 if (set_stat_dos_flags(conn, smb_fname, dosmode,
744 &attributes_changed))
746 if (!newfile && attributes_changed) {
747 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
748 FILE_NOTIFY_CHANGE_ATTRIBUTES,
749 smb_fname->base_name);
751 smb_fname->st.st_ex_mode = unixmode;
752 return 0;
755 #endif
756 /* Store the DOS attributes in an EA by preference. */
757 if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
758 if (!newfile) {
759 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
760 FILE_NOTIFY_CHANGE_ATTRIBUTES,
761 smb_fname->base_name);
763 smb_fname->st.st_ex_mode = unixmode;
764 return 0;
767 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
769 /* preserve the s bits */
770 mask |= (S_ISUID | S_ISGID);
772 /* preserve the t bit */
773 #ifdef S_ISVTX
774 mask |= S_ISVTX;
775 #endif
777 /* possibly preserve the x bits */
778 if (!MAP_ARCHIVE(conn))
779 mask |= S_IXUSR;
780 if (!MAP_SYSTEM(conn))
781 mask |= S_IXGRP;
782 if (!MAP_HIDDEN(conn))
783 mask |= S_IXOTH;
785 unixmode |= (smb_fname->st.st_ex_mode & mask);
787 /* if we previously had any r bits set then leave them alone */
788 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
789 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
790 unixmode |= tmp;
793 /* if we previously had any w bits set then leave them alone
794 whilst adding in the new w bits, if the new mode is not rdonly */
795 if (!IS_DOS_READONLY(dosmode)) {
796 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
799 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
800 if (ret == 0) {
801 if(!newfile || (lret != -1)) {
802 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
803 FILE_NOTIFY_CHANGE_ATTRIBUTES,
804 smb_fname->base_name);
806 smb_fname->st.st_ex_mode = unixmode;
807 return 0;
810 if((errno != EPERM) && (errno != EACCES))
811 return -1;
813 if(!lp_dos_filemode(SNUM(conn)))
814 return -1;
816 /* We want DOS semantics, ie allow non owner with write permission to change the
817 bits on a file. Just like file_ntimes below.
820 /* Check if we have write access. */
821 if (CAN_WRITE(conn)) {
823 * We need to open the file with write access whilst
824 * still in our current user context. This ensures we
825 * are not violating security in doing the fchmod.
826 * This file open does *not* break any oplocks we are
827 * holding. We need to review this.... may need to
828 * break batch oplocks open by others. JRA.
830 files_struct *fsp;
831 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname,
832 &fsp)))
833 return -1;
834 become_root();
835 ret = SMB_VFS_FCHMOD(fsp, unixmode);
836 unbecome_root();
837 close_file_fchmod(NULL, fsp);
838 if (!newfile) {
839 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
840 FILE_NOTIFY_CHANGE_ATTRIBUTES,
841 smb_fname->base_name);
843 if (ret == 0) {
844 smb_fname->st.st_ex_mode = unixmode;
848 return( ret );
851 /*******************************************************************
852 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
853 than POSIX.
854 *******************************************************************/
856 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
857 struct smb_file_time *ft)
859 int ret = -1;
861 errno = 0;
863 DEBUG(6, ("file_ntime: actime: %s",
864 time_to_asc(convert_timespec_to_time_t(ft->atime))));
865 DEBUG(6, ("file_ntime: modtime: %s",
866 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
867 DEBUG(6, ("file_ntime: ctime: %s",
868 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
869 DEBUG(6, ("file_ntime: createtime: %s",
870 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
872 /* Don't update the time on read-only shares */
873 /* We need this as set_filetime (which can be called on
874 close and other paths) can end up calling this function
875 without the NEED_WRITE protection. Found by :
876 Leo Weppelman <leo@wau.mis.ah.nl>
879 if (!CAN_WRITE(conn)) {
880 return 0;
883 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
884 return 0;
887 if((errno != EPERM) && (errno != EACCES)) {
888 return -1;
891 if(!lp_dos_filetimes(SNUM(conn))) {
892 return -1;
895 /* We have permission (given by the Samba admin) to
896 break POSIX semantics and allow a user to change
897 the time on a file they don't own but can write to
898 (as DOS does).
901 /* Check if we have write access. */
902 if (can_write_to_file(conn, smb_fname)) {
903 /* We are allowed to become root and change the filetime. */
904 become_root();
905 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
906 unbecome_root();
909 return ret;
912 /******************************************************************
913 Force a "sticky" write time on a pathname. This will always be
914 returned on all future write time queries and set on close.
915 ******************************************************************/
917 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
919 if (null_timespec(mtime)) {
920 return true;
923 if (!set_sticky_write_time(fileid, mtime)) {
924 return false;
927 return true;
930 /******************************************************************
931 Force a "sticky" write time on an fsp. This will always be
932 returned on all future write time queries and set on close.
933 ******************************************************************/
935 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
937 if (null_timespec(mtime)) {
938 return true;
941 fsp->write_time_forced = true;
942 TALLOC_FREE(fsp->update_write_time_event);
944 return set_sticky_write_time_path(fsp->file_id, mtime);
947 /******************************************************************
948 Set a create time EA.
949 ******************************************************************/
951 NTSTATUS set_create_timespec_ea(connection_struct *conn,
952 const struct smb_filename *psmb_fname,
953 struct timespec create_time)
955 NTSTATUS status;
956 struct smb_filename *smb_fname = NULL;
957 uint32_t dosmode;
958 int ret;
960 if (!lp_store_dos_attributes(SNUM(conn))) {
961 return NT_STATUS_OK;
964 status = create_synthetic_smb_fname(talloc_tos(),
965 psmb_fname->base_name,
966 NULL, &psmb_fname->st,
967 &smb_fname);
969 if (!NT_STATUS_IS_OK(status)) {
970 return status;
973 dosmode = dos_mode(conn, smb_fname);
975 smb_fname->st.st_ex_btime = create_time;
977 ret = file_set_dosmode(conn, smb_fname, dosmode, NULL, false);
978 if (ret == -1) {
979 map_nt_error_from_unix(errno);
982 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
983 smb_fname_str_dbg(smb_fname)));
985 return NT_STATUS_OK;
988 /******************************************************************
989 Return a create time.
990 ******************************************************************/
992 struct timespec get_create_timespec(connection_struct *conn,
993 struct files_struct *fsp,
994 const struct smb_filename *smb_fname)
996 return smb_fname->st.st_ex_btime;
999 /******************************************************************
1000 Return a change time (may look at EA in future).
1001 ******************************************************************/
1003 struct timespec get_change_timespec(connection_struct *conn,
1004 struct files_struct *fsp,
1005 const struct smb_filename *smb_fname)
1007 return smb_fname->st.st_ex_mtime;