Filter the returned DOS attributes by 0xFF for clients
[Samba/ekacnet.git] / source3 / smbd / dosmode.c
blob199a67371faa3ea0796d8060c8dedc63229023d9
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"
23 static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
25 #if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
26 if (sbuf->st_ex_size > sbuf->st_ex_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
27 return FILE_ATTRIBUTE_SPARSE;
29 #endif
30 return 0;
33 static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
35 #ifdef S_ISLNK
36 #if LINKS_READ_ONLY
37 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
38 return aRONLY;
39 #endif
40 #endif
41 return 0;
44 /****************************************************************************
45 Change a dos mode to a unix mode.
46 Base permission for files:
47 if creating file and inheriting (i.e. parent_dir != NULL)
48 apply read/write bits from parent directory.
49 else
50 everybody gets read bit set
51 dos readonly is represented in unix by removing everyone's write bit
52 dos archive is represented in unix by the user's execute bit
53 dos system is represented in unix by the group's execute bit
54 dos hidden is represented in unix by the other's execute bit
55 if !inheriting {
56 Then apply create mask,
57 then add force bits.
59 Base permission for directories:
60 dos directory is represented in unix by unix's dir bit and the exec bit
61 if !inheriting {
62 Then apply create mask,
63 then add force bits.
65 ****************************************************************************/
67 mode_t unix_mode(connection_struct *conn, int dosmode,
68 const struct smb_filename *smb_fname,
69 const char *inherit_from_dir)
71 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
72 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
73 * inheriting. */
75 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
76 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
79 if ((inherit_from_dir != NULL) && lp_inherit_perms(SNUM(conn))) {
80 struct smb_filename *smb_fname_parent = NULL;
81 NTSTATUS status;
83 DEBUG(2, ("unix_mode(%s) inheriting from %s\n",
84 smb_fname_str_dbg(smb_fname),
85 inherit_from_dir));
87 status = create_synthetic_smb_fname(talloc_tos(),
88 inherit_from_dir, NULL,
89 NULL, &smb_fname_parent);
90 if (!NT_STATUS_IS_OK(status)) {
91 DEBUG(1,("unix_mode(%s) failed, [dir %s]: %s\n",
92 smb_fname_str_dbg(smb_fname),
93 inherit_from_dir, nt_errstr(status)));
94 return(0);
97 if (SMB_VFS_STAT(conn, smb_fname_parent) != 0) {
98 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",
99 smb_fname_str_dbg(smb_fname),
100 inherit_from_dir, strerror(errno)));
101 TALLOC_FREE(smb_fname_parent);
102 return(0); /* *** shouldn't happen! *** */
105 /* Save for later - but explicitly remove setuid bit for safety. */
106 dir_mode = smb_fname_parent->st.st_ex_mode & ~S_ISUID;
107 DEBUG(2,("unix_mode(%s) inherit mode %o\n",
108 smb_fname_str_dbg(smb_fname), (int)dir_mode));
109 /* Clear "result" */
110 result = 0;
111 TALLOC_FREE(smb_fname_parent);
114 if (IS_DOS_DIR(dosmode)) {
115 /* We never make directories read only for the owner as under DOS a user
116 can always create a file in a read-only directory. */
117 result |= (S_IFDIR | S_IWUSR);
119 if (dir_mode) {
120 /* Inherit mode of parent directory. */
121 result |= dir_mode;
122 } else {
123 /* Provisionally add all 'x' bits */
124 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
126 /* Apply directory mask */
127 result &= lp_dir_mask(SNUM(conn));
128 /* Add in force bits */
129 result |= lp_force_dir_mode(SNUM(conn));
131 } else {
132 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
133 result |= S_IXUSR;
135 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
136 result |= S_IXGRP;
138 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
139 result |= S_IXOTH;
141 if (dir_mode) {
142 /* Inherit 666 component of parent directory mode */
143 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
144 } else {
145 /* Apply mode mask */
146 result &= lp_create_mask(SNUM(conn));
147 /* Add in force bits */
148 result |= lp_force_create_mode(SNUM(conn));
152 DEBUG(3,("unix_mode(%s) returning 0%o\n", smb_fname_str_dbg(smb_fname),
153 (int)result));
154 return(result);
157 /****************************************************************************
158 Change a unix mode to a dos mode.
159 ****************************************************************************/
161 static uint32 dos_mode_from_sbuf(connection_struct *conn,
162 const struct smb_filename *smb_fname)
164 int result = 0;
165 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
167 if (ro_opts == MAP_READONLY_YES) {
168 /* Original Samba method - map inverse of user "w" bit. */
169 if ((smb_fname->st.st_ex_mode & S_IWUSR) == 0) {
170 result |= aRONLY;
172 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
173 /* Check actual permissions for read-only. */
174 if (!can_write_to_file(conn, smb_fname)) {
175 result |= aRONLY;
177 } /* Else never set the readonly bit. */
179 if (MAP_ARCHIVE(conn) && ((smb_fname->st.st_ex_mode & S_IXUSR) != 0))
180 result |= aARCH;
182 if (MAP_SYSTEM(conn) && ((smb_fname->st.st_ex_mode & S_IXGRP) != 0))
183 result |= aSYSTEM;
185 if (MAP_HIDDEN(conn) && ((smb_fname->st.st_ex_mode & S_IXOTH) != 0))
186 result |= aHIDDEN;
188 if (S_ISDIR(smb_fname->st.st_ex_mode))
189 result = aDIR | (result & aRONLY);
191 result |= set_sparse_flag(&smb_fname->st);
192 result |= set_link_read_only_flag(&smb_fname->st);
194 DEBUG(8,("dos_mode_from_sbuf returning "));
196 if (result & aHIDDEN) DEBUG(8, ("h"));
197 if (result & aRONLY ) DEBUG(8, ("r"));
198 if (result & aSYSTEM) DEBUG(8, ("s"));
199 if (result & aDIR ) DEBUG(8, ("d"));
200 if (result & aARCH ) DEBUG(8, ("a"));
202 DEBUG(8,("\n"));
203 return result;
206 /****************************************************************************
207 Get DOS attributes from an EA.
208 ****************************************************************************/
210 static bool get_ea_dos_attribute(connection_struct *conn,
211 const struct smb_filename *smb_fname,
212 uint32 *pattr)
214 ssize_t sizeret;
215 fstring attrstr;
216 unsigned int dosattr;
218 if (!lp_store_dos_attributes(SNUM(conn))) {
219 return False;
222 /* Don't reset pattr to zero as we may already have filename-based attributes we
223 need to preserve. */
225 sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name,
226 SAMBA_XATTR_DOS_ATTRIB, attrstr,
227 sizeof(attrstr));
228 if (sizeret == -1) {
229 if (errno == ENOSYS
230 #if defined(ENOTSUP)
231 || errno == ENOTSUP) {
232 #else
234 #endif
235 DEBUG(1,("get_ea_dos_attributes: Cannot get attribute "
236 "from EA on file %s: Error = %s\n",
237 smb_fname_str_dbg(smb_fname),
238 strerror(errno)));
239 set_store_dos_attributes(SNUM(conn), False);
241 return False;
243 /* Null terminate string. */
244 attrstr[sizeret] = 0;
245 DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n",
246 smb_fname_str_dbg(smb_fname), attrstr));
248 if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
249 sscanf(attrstr, "%x", &dosattr) != 1) {
250 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on "
251 "file %s - %s\n", smb_fname_str_dbg(smb_fname),
252 attrstr));
253 return False;
256 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
257 dosattr |= aDIR;
259 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
261 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
263 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
264 if (dosattr & aRONLY ) DEBUG(8, ("r"));
265 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
266 if (dosattr & aDIR ) DEBUG(8, ("d"));
267 if (dosattr & aARCH ) DEBUG(8, ("a"));
269 DEBUG(8,("\n"));
271 return True;
274 /****************************************************************************
275 Set DOS attributes in an EA.
276 ****************************************************************************/
278 static bool set_ea_dos_attribute(connection_struct *conn,
279 struct smb_filename *smb_fname,
280 uint32 dosmode)
282 fstring attrstr;
283 files_struct *fsp = NULL;
284 bool ret = False;
286 if (!lp_store_dos_attributes(SNUM(conn))) {
287 return False;
290 snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
291 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
292 SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr),
293 0) == -1) {
294 if((errno != EPERM) && (errno != EACCES)) {
295 if (errno == ENOSYS
296 #if defined(ENOTSUP)
297 || errno == ENOTSUP) {
298 #else
300 #endif
301 DEBUG(1,("set_ea_dos_attributes: Cannot set "
302 "attribute EA on file %s: Error = %s\n",
303 smb_fname_str_dbg(smb_fname),
304 strerror(errno) ));
305 set_store_dos_attributes(SNUM(conn), False);
307 return False;
310 /* We want DOS semantics, ie allow non owner with write permission to change the
311 bits on a file. Just like file_ntimes below.
314 /* Check if we have write access. */
315 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
316 return False;
319 * We need to open the file with write access whilst
320 * still in our current user context. This ensures we
321 * are not violating security in doing the setxattr.
324 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname,
325 &fsp)))
326 return ret;
327 become_root();
328 if (SMB_VFS_SETXATTR(conn, smb_fname->base_name,
329 SAMBA_XATTR_DOS_ATTRIB, attrstr,
330 strlen(attrstr), 0) == 0) {
331 ret = True;
333 unbecome_root();
334 close_file_fchmod(NULL, fsp);
335 return ret;
337 DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr,
338 smb_fname_str_dbg(smb_fname)));
339 return True;
342 /****************************************************************************
343 Change a unix mode to a dos mode for an ms dfs link.
344 ****************************************************************************/
346 uint32 dos_mode_msdfs(connection_struct *conn,
347 const struct smb_filename *smb_fname)
349 uint32 result = 0;
351 DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname)));
353 if (!VALID_STAT(smb_fname->st)) {
354 return 0;
357 /* First do any modifications that depend on the path name. */
358 /* hide files with a name starting with a . */
359 if (lp_hide_dot_files(SNUM(conn))) {
360 const char *p = strrchr_m(smb_fname->base_name, '/');
361 if (p) {
362 p++;
363 } else {
364 p = smb_fname->base_name;
367 /* Only . and .. are not hidden. */
368 if (p[0] == '.' && !((p[1] == '\0') ||
369 (p[1] == '.' && p[2] == '\0'))) {
370 result |= aHIDDEN;
374 result |= dos_mode_from_sbuf(conn, smb_fname);
376 /* Optimization : Only call is_hidden_path if it's not already
377 hidden. */
378 if (!(result & aHIDDEN) &&
379 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
380 result |= aHIDDEN;
383 if (get_Protocol() <= PROTOCOL_LANMAN2) {
384 DEBUG(10,("dos_mode_msdfs : filtering protocol 0x%x to 0xff\n",
385 (unsigned int)result ));
386 result &= 0xff;
389 DEBUG(8,("dos_mode_msdfs returning "));
391 if (result & aHIDDEN) DEBUG(8, ("h"));
392 if (result & aRONLY ) DEBUG(8, ("r"));
393 if (result & aSYSTEM) DEBUG(8, ("s"));
394 if (result & aDIR ) DEBUG(8, ("d"));
395 if (result & aARCH ) DEBUG(8, ("a"));
396 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
398 DEBUG(8,("\n"));
400 return(result);
403 #ifdef HAVE_STAT_DOS_FLAGS
404 /****************************************************************************
405 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
406 ****************************************************************************/
408 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
410 uint32_t dos_stat_flags = 0;
412 if (dosmode & aARCH)
413 dos_stat_flags |= UF_DOS_ARCHIVE;
414 if (dosmode & aHIDDEN)
415 dos_stat_flags |= UF_DOS_HIDDEN;
416 if (dosmode & aRONLY)
417 dos_stat_flags |= UF_DOS_RO;
418 if (dosmode & aSYSTEM)
419 dos_stat_flags |= UF_DOS_SYSTEM;
420 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
421 dos_stat_flags |= UF_DOS_NOINDEX;
423 return dos_stat_flags;
426 /****************************************************************************
427 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
428 ****************************************************************************/
430 static bool get_stat_dos_flags(connection_struct *conn,
431 const struct smb_filename *smb_fname,
432 uint32_t *dosmode)
434 SMB_ASSERT(VALID_STAT(smb_fname->st));
435 SMB_ASSERT(dosmode);
437 if (!lp_store_dos_attributes(SNUM(conn))) {
438 return false;
441 DEBUG(5, ("Getting stat dos attributes for %s.\n",
442 smb_fname_str_dbg(smb_fname)));
444 if (smb_fname->st.st_ex_flags & UF_DOS_ARCHIVE)
445 *dosmode |= aARCH;
446 if (smb_fname->st.st_ex_flags & UF_DOS_HIDDEN)
447 *dosmode |= aHIDDEN;
448 if (smb_fname->st.st_ex_flags & UF_DOS_RO)
449 *dosmode |= aRONLY;
450 if (smb_fname->st.st_ex_flags & UF_DOS_SYSTEM)
451 *dosmode |= aSYSTEM;
452 if (smb_fname->st.st_ex_flags & UF_DOS_NOINDEX)
453 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
454 if (S_ISDIR(smb_fname->st.st_ex_mode))
455 *dosmode |= aDIR;
457 *dosmode |= set_sparse_flag(&smb_fname->st);
458 *dosmode |= set_link_read_only_flag(&smb_fname->st);
460 return true;
463 /****************************************************************************
464 Sets DOS attributes, stored in st_ex_flags of the inode.
465 ****************************************************************************/
467 static bool set_stat_dos_flags(connection_struct *conn,
468 const struct smb_filename *smb_fname,
469 uint32_t dosmode,
470 bool *attributes_changed)
472 uint32_t new_flags = 0;
473 int error = 0;
475 SMB_ASSERT(VALID_STAT(smb_fname->st));
476 SMB_ASSERT(attributes_changed);
478 *attributes_changed = false;
480 if (!lp_store_dos_attributes(SNUM(conn))) {
481 return false;
484 DEBUG(5, ("Setting stat dos attributes for %s.\n",
485 smb_fname_str_dbg(smb_fname)));
487 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
488 dos_attributes_to_stat_dos_flags(dosmode);
490 /* Return early if no flags changed. */
491 if (new_flags == smb_fname->st.st_ex_flags)
492 return true;
494 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
495 smb_fname->st.st_ex_flags));
497 /* Set new flags with chflags. */
498 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
499 if (error) {
500 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
501 "file %s! errno=%d\n", new_flags,
502 smb_fname_str_dbg(smb_fname), errno));
503 return false;
506 *attributes_changed = true;
507 return true;
509 #endif /* HAVE_STAT_DOS_FLAGS */
511 /****************************************************************************
512 Change a unix mode to a dos mode.
513 ****************************************************************************/
515 uint32 dos_mode(connection_struct *conn, const struct smb_filename *smb_fname)
517 SMB_STRUCT_STAT sbuf;
518 uint32 result = 0;
519 bool offline, used_stat_dos_flags = false;
521 DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname)));
523 if (!VALID_STAT(smb_fname->st)) {
524 return 0;
527 /* First do any modifications that depend on the path name. */
528 /* hide files with a name starting with a . */
529 if (lp_hide_dot_files(SNUM(conn))) {
530 const char *p = strrchr_m(smb_fname->base_name,'/');
531 if (p) {
532 p++;
533 } else {
534 p = smb_fname->base_name;
537 /* Only . and .. are not hidden. */
538 if (p[0] == '.' && !((p[1] == '\0') ||
539 (p[1] == '.' && p[2] == '\0'))) {
540 result |= aHIDDEN;
544 #ifdef HAVE_STAT_DOS_FLAGS
545 used_stat_dos_flags = get_stat_dos_flags(conn, smb_fname, &result);
546 #endif
547 if (!used_stat_dos_flags) {
548 /* Get the DOS attributes from an EA by preference. */
549 if (get_ea_dos_attribute(conn, smb_fname, &result)) {
550 result |= set_sparse_flag(&smb_fname->st);
551 } else {
552 result |= dos_mode_from_sbuf(conn, smb_fname);
556 sbuf = smb_fname->st;
557 offline = SMB_VFS_IS_OFFLINE(conn, smb_fname->base_name, &sbuf);
558 if (S_ISREG(sbuf.st_ex_mode) && offline) {
559 result |= FILE_ATTRIBUTE_OFFLINE;
562 /* Optimization : Only call is_hidden_path if it's not already
563 hidden. */
564 if (!(result & aHIDDEN) &&
565 IS_HIDDEN_PATH(conn, smb_fname->base_name)) {
566 result |= aHIDDEN;
569 if (get_Protocol() <= PROTOCOL_LANMAN2) {
570 DEBUG(10,("dos_mode : filtering protocol 0x%x to 0xff\n",
571 (unsigned int)result ));
572 result &= 0xff;
575 DEBUG(8,("dos_mode returning "));
577 if (result & aHIDDEN) DEBUG(8, ("h"));
578 if (result & aRONLY ) DEBUG(8, ("r"));
579 if (result & aSYSTEM) DEBUG(8, ("s"));
580 if (result & aDIR ) DEBUG(8, ("d"));
581 if (result & aARCH ) DEBUG(8, ("a"));
582 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
584 DEBUG(8,("\n"));
586 return(result);
589 /*******************************************************************
590 chmod a file - but preserve some bits.
591 ********************************************************************/
593 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
594 uint32 dosmode, const char *parent_dir, bool newfile)
596 int mask=0;
597 mode_t tmp;
598 mode_t unixmode;
599 int ret = -1, lret = -1;
600 uint32_t old_mode;
602 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
603 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
605 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
606 dosmode, smb_fname_str_dbg(smb_fname)));
608 if (!VALID_STAT(smb_fname->st)) {
609 if (SMB_VFS_STAT(conn, smb_fname))
610 return(-1);
613 unixmode = smb_fname->st.st_ex_mode;
615 get_acl_group_bits(conn, smb_fname->base_name,
616 &smb_fname->st.st_ex_mode);
618 if (S_ISDIR(smb_fname->st.st_ex_mode))
619 dosmode |= aDIR;
620 else
621 dosmode &= ~aDIR;
623 old_mode = dos_mode(conn, smb_fname);
625 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
626 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
627 lret = SMB_VFS_SET_OFFLINE(conn, smb_fname->base_name);
628 if (lret == -1) {
629 DEBUG(0, ("set_dos_mode: client has asked to "
630 "set FILE_ATTRIBUTE_OFFLINE to "
631 "%s/%s but there was an error while "
632 "setting it or it is not "
633 "supported.\n", parent_dir,
634 smb_fname_str_dbg(smb_fname)));
639 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
640 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
642 if (old_mode == dosmode) {
643 smb_fname->st.st_ex_mode = unixmode;
644 return(0);
647 #ifdef HAVE_STAT_DOS_FLAGS
649 bool attributes_changed;
651 if (set_stat_dos_flags(conn, smb_fname, dosmode,
652 &attributes_changed))
654 if (!newfile && attributes_changed) {
655 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
656 FILE_NOTIFY_CHANGE_ATTRIBUTES,
657 smb_fname->base_name);
659 smb_fname->st.st_ex_mode = unixmode;
660 return 0;
663 #endif
664 /* Store the DOS attributes in an EA by preference. */
665 if (set_ea_dos_attribute(conn, smb_fname, dosmode)) {
666 if (!newfile) {
667 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
668 FILE_NOTIFY_CHANGE_ATTRIBUTES,
669 smb_fname->base_name);
671 smb_fname->st.st_ex_mode = unixmode;
672 return 0;
675 unixmode = unix_mode(conn, dosmode, smb_fname, parent_dir);
677 /* preserve the s bits */
678 mask |= (S_ISUID | S_ISGID);
680 /* preserve the t bit */
681 #ifdef S_ISVTX
682 mask |= S_ISVTX;
683 #endif
685 /* possibly preserve the x bits */
686 if (!MAP_ARCHIVE(conn))
687 mask |= S_IXUSR;
688 if (!MAP_SYSTEM(conn))
689 mask |= S_IXGRP;
690 if (!MAP_HIDDEN(conn))
691 mask |= S_IXOTH;
693 unixmode |= (smb_fname->st.st_ex_mode & mask);
695 /* if we previously had any r bits set then leave them alone */
696 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
697 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
698 unixmode |= tmp;
701 /* if we previously had any w bits set then leave them alone
702 whilst adding in the new w bits, if the new mode is not rdonly */
703 if (!IS_DOS_READONLY(dosmode)) {
704 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
707 ret = SMB_VFS_CHMOD(conn, smb_fname->base_name, unixmode);
708 if (ret == 0) {
709 if(!newfile || (lret != -1)) {
710 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
711 FILE_NOTIFY_CHANGE_ATTRIBUTES,
712 smb_fname->base_name);
714 smb_fname->st.st_ex_mode = unixmode;
715 return 0;
718 if((errno != EPERM) && (errno != EACCES))
719 return -1;
721 if(!lp_dos_filemode(SNUM(conn)))
722 return -1;
724 /* We want DOS semantics, ie allow non owner with write permission to change the
725 bits on a file. Just like file_ntimes below.
728 /* Check if we have write access. */
729 if (CAN_WRITE(conn)) {
731 * We need to open the file with write access whilst
732 * still in our current user context. This ensures we
733 * are not violating security in doing the fchmod.
734 * This file open does *not* break any oplocks we are
735 * holding. We need to review this.... may need to
736 * break batch oplocks open by others. JRA.
738 files_struct *fsp;
739 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, smb_fname,
740 &fsp)))
741 return -1;
742 become_root();
743 ret = SMB_VFS_FCHMOD(fsp, unixmode);
744 unbecome_root();
745 close_file_fchmod(NULL, fsp);
746 if (!newfile) {
747 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
748 FILE_NOTIFY_CHANGE_ATTRIBUTES,
749 smb_fname->base_name);
751 if (ret == 0) {
752 smb_fname->st.st_ex_mode = unixmode;
756 return( ret );
759 /*******************************************************************
760 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
761 than POSIX.
762 *******************************************************************/
764 int file_ntimes(connection_struct *conn, const struct smb_filename *smb_fname,
765 struct smb_file_time *ft)
767 int ret = -1;
769 errno = 0;
771 DEBUG(6, ("file_ntime: actime: %s",
772 time_to_asc(convert_timespec_to_time_t(ft->atime))));
773 DEBUG(6, ("file_ntime: modtime: %s",
774 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
775 DEBUG(6, ("file_ntime: ctime: %s",
776 time_to_asc(convert_timespec_to_time_t(ft->ctime))));
777 DEBUG(6, ("file_ntime: createtime: %s",
778 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
780 /* Don't update the time on read-only shares */
781 /* We need this as set_filetime (which can be called on
782 close and other paths) can end up calling this function
783 without the NEED_WRITE protection. Found by :
784 Leo Weppelman <leo@wau.mis.ah.nl>
787 if (!CAN_WRITE(conn)) {
788 return 0;
791 if(SMB_VFS_NTIMES(conn, smb_fname, ft) == 0) {
792 return 0;
795 if((errno != EPERM) && (errno != EACCES)) {
796 return -1;
799 if(!lp_dos_filetimes(SNUM(conn))) {
800 return -1;
803 /* We have permission (given by the Samba admin) to
804 break POSIX semantics and allow a user to change
805 the time on a file they don't own but can write to
806 (as DOS does).
809 /* Check if we have write access. */
810 if (can_write_to_file(conn, smb_fname)) {
811 /* We are allowed to become root and change the filetime. */
812 become_root();
813 ret = SMB_VFS_NTIMES(conn, smb_fname, ft);
814 unbecome_root();
817 return ret;
820 /******************************************************************
821 Force a "sticky" write time on a pathname. This will always be
822 returned on all future write time queries and set on close.
823 ******************************************************************/
825 bool set_sticky_write_time_path(struct file_id fileid, struct timespec mtime)
827 if (null_timespec(mtime)) {
828 return true;
831 if (!set_sticky_write_time(fileid, mtime)) {
832 return false;
835 return true;
838 /******************************************************************
839 Force a "sticky" write time on an fsp. This will always be
840 returned on all future write time queries and set on close.
841 ******************************************************************/
843 bool set_sticky_write_time_fsp(struct files_struct *fsp, struct timespec mtime)
845 fsp->write_time_forced = true;
846 TALLOC_FREE(fsp->update_write_time_event);
848 return set_sticky_write_time_path(fsp->file_id, mtime);
851 /******************************************************************
852 Update a write time immediately, without the 2 second delay.
853 ******************************************************************/
855 bool update_write_time(struct files_struct *fsp)
857 if (!set_write_time(fsp->file_id, timespec_current())) {
858 return false;
861 notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
862 FILE_NOTIFY_CHANGE_LAST_WRITE, fsp->fsp_name->base_name);
864 return true;
867 /******************************************************************
868 Set a create time EA.
869 ******************************************************************/
871 NTSTATUS set_create_timespec_ea(connection_struct *conn,
872 struct files_struct *fsp,
873 const struct smb_filename *smb_fname,
874 struct timespec create_time)
876 int ret;
877 char buf[8];
879 if (!lp_store_create_time(SNUM(conn))) {
880 return NT_STATUS_OK;
883 put_long_date_timespec(conn->ts_res, buf, create_time);
884 if (fsp && fsp->fh->fd != -1) {
885 ret = SMB_VFS_FSETXATTR(fsp,
886 SAMBA_XATTR_DOSTIMESTAMPS,
887 buf,
888 sizeof(buf),
890 } else {
891 ret = SMB_VFS_SETXATTR(conn,
892 smb_fname->base_name,
893 SAMBA_XATTR_DOSTIMESTAMPS,
894 buf,
895 sizeof(buf),
899 if (ret == -1) {
900 map_nt_error_from_unix(errno);
902 DEBUG(10,("set_create_timespec_ea: wrote create time EA for file %s\n",
903 smb_fname_str_dbg(smb_fname)));
904 return NT_STATUS_OK;
907 /******************************************************************
908 Returns an EA create timespec, or a zero timespec if fail.
909 ******************************************************************/
911 static struct timespec get_create_timespec_ea(connection_struct *conn,
912 struct files_struct *fsp,
913 const struct smb_filename *smb_fname)
915 ssize_t ret;
916 char buf[8];
917 struct timespec ts;
919 ZERO_STRUCT(ts);
921 if (!lp_store_create_time(SNUM(conn))) {
922 return ts;
925 if (fsp && fsp->fh->fd != -1) {
926 ret = SMB_VFS_FGETXATTR(fsp,
927 SAMBA_XATTR_DOSTIMESTAMPS,
928 buf,
929 sizeof(buf));
930 } else {
931 ret = SMB_VFS_GETXATTR(conn,
932 smb_fname->base_name,
933 SAMBA_XATTR_DOSTIMESTAMPS,
934 buf,
935 sizeof(buf));
937 if (ret == sizeof(buf)) {
938 return interpret_long_date(buf);
939 } else {
940 return ts;
944 /******************************************************************
945 Return a create time - looks at EA.
946 ******************************************************************/
948 struct timespec get_create_timespec(connection_struct *conn,
949 struct files_struct *fsp,
950 const struct smb_filename *smb_fname)
952 struct timespec ts = get_create_timespec_ea(conn, fsp, smb_fname);
954 if (!null_timespec(ts)) {
955 return ts;
956 } else {
957 return smb_fname->st.st_ex_btime;
961 /******************************************************************
962 Return a change time (may look at EA in future).
963 ******************************************************************/
965 struct timespec get_change_timespec(connection_struct *conn,
966 struct files_struct *fsp,
967 const struct smb_filename *smb_fname)
969 return smb_fname->st.st_ex_mtime;