s3: Plumb smb_filename around SMB_VFS_CHFLAGS
[Samba/fernandojvsilva.git] / source3 / smbd / dosmode.c
blob2006b75d527850598e8a83a53dcd9a0ba523e7c6
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, const char *fname,
68 const char *inherit_from_dir)
70 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
71 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
72 * inheriting. */
74 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
75 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
78 if (fname && (inherit_from_dir != NULL)
79 && lp_inherit_perms(SNUM(conn))) {
80 SMB_STRUCT_STAT sbuf;
82 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
83 inherit_from_dir));
84 if (vfs_stat_smb_fname(conn, inherit_from_dir, &sbuf) != 0) {
85 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname,
86 inherit_from_dir, strerror(errno)));
87 return(0); /* *** shouldn't happen! *** */
90 /* Save for later - but explicitly remove setuid bit for safety. */
91 dir_mode = sbuf.st_ex_mode & ~S_ISUID;
92 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
93 /* Clear "result" */
94 result = 0;
97 if (IS_DOS_DIR(dosmode)) {
98 /* We never make directories read only for the owner as under DOS a user
99 can always create a file in a read-only directory. */
100 result |= (S_IFDIR | S_IWUSR);
102 if (dir_mode) {
103 /* Inherit mode of parent directory. */
104 result |= dir_mode;
105 } else {
106 /* Provisionally add all 'x' bits */
107 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
109 /* Apply directory mask */
110 result &= lp_dir_mask(SNUM(conn));
111 /* Add in force bits */
112 result |= lp_force_dir_mode(SNUM(conn));
114 } else {
115 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
116 result |= S_IXUSR;
118 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
119 result |= S_IXGRP;
121 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
122 result |= S_IXOTH;
124 if (dir_mode) {
125 /* Inherit 666 component of parent directory mode */
126 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
127 } else {
128 /* Apply mode mask */
129 result &= lp_create_mask(SNUM(conn));
130 /* Add in force bits */
131 result |= lp_force_create_mode(SNUM(conn));
135 DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
136 return(result);
139 /****************************************************************************
140 Change a unix mode to a dos mode.
141 ****************************************************************************/
143 static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, const SMB_STRUCT_STAT *sbuf)
145 int result = 0;
146 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
148 if (ro_opts == MAP_READONLY_YES) {
149 /* Original Samba method - map inverse of user "w" bit. */
150 if ((sbuf->st_ex_mode & S_IWUSR) == 0) {
151 result |= aRONLY;
153 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
154 struct smb_filename *smb_fname = NULL;
155 NTSTATUS status;
157 status = create_synthetic_smb_fname_split(talloc_tos(), path,
158 sbuf, &smb_fname);
159 if (NT_STATUS_IS_OK(status)) {
160 /* Check actual permissions for read-only. */
161 if (!can_write_to_file(conn, smb_fname)) {
162 result |= aRONLY;
165 TALLOC_FREE(smb_fname);
166 } /* Else never set the readonly bit. */
168 if (MAP_ARCHIVE(conn) && ((sbuf->st_ex_mode & S_IXUSR) != 0))
169 result |= aARCH;
171 if (MAP_SYSTEM(conn) && ((sbuf->st_ex_mode & S_IXGRP) != 0))
172 result |= aSYSTEM;
174 if (MAP_HIDDEN(conn) && ((sbuf->st_ex_mode & S_IXOTH) != 0))
175 result |= aHIDDEN;
177 if (S_ISDIR(sbuf->st_ex_mode))
178 result = aDIR | (result & aRONLY);
180 result |= set_sparse_flag(sbuf);
181 result |= set_link_read_only_flag(sbuf);
183 DEBUG(8,("dos_mode_from_sbuf returning "));
185 if (result & aHIDDEN) DEBUG(8, ("h"));
186 if (result & aRONLY ) DEBUG(8, ("r"));
187 if (result & aSYSTEM) DEBUG(8, ("s"));
188 if (result & aDIR ) DEBUG(8, ("d"));
189 if (result & aARCH ) DEBUG(8, ("a"));
191 DEBUG(8,("\n"));
192 return result;
195 /****************************************************************************
196 Get DOS attributes from an EA.
197 ****************************************************************************/
199 static bool get_ea_dos_attribute(connection_struct *conn, const char *path, const SMB_STRUCT_STAT *sbuf, uint32 *pattr)
201 ssize_t sizeret;
202 fstring attrstr;
203 unsigned int dosattr;
205 if (!lp_store_dos_attributes(SNUM(conn))) {
206 return False;
209 /* Don't reset pattr to zero as we may already have filename-based attributes we
210 need to preserve. */
212 sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
213 if (sizeret == -1) {
214 if (errno == ENOSYS
215 #if defined(ENOTSUP)
216 || errno == ENOTSUP) {
217 #else
219 #endif
220 DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
221 path, strerror(errno) ));
222 set_store_dos_attributes(SNUM(conn), False);
224 return False;
226 /* Null terminate string. */
227 attrstr[sizeret] = 0;
228 DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
230 if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
231 sscanf(attrstr, "%x", &dosattr) != 1) {
232 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
233 return False;
236 if (S_ISDIR(sbuf->st_ex_mode)) {
237 dosattr |= aDIR;
239 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
241 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
243 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
244 if (dosattr & aRONLY ) DEBUG(8, ("r"));
245 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
246 if (dosattr & aDIR ) DEBUG(8, ("d"));
247 if (dosattr & aARCH ) DEBUG(8, ("a"));
249 DEBUG(8,("\n"));
251 return True;
254 /****************************************************************************
255 Set DOS attributes in an EA.
256 ****************************************************************************/
258 static bool set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
260 fstring attrstr;
261 files_struct *fsp = NULL;
262 bool ret = False;
264 if (!lp_store_dos_attributes(SNUM(conn))) {
265 return False;
268 snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
269 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
270 if((errno != EPERM) && (errno != EACCES)) {
271 if (errno == ENOSYS
272 #if defined(ENOTSUP)
273 || errno == ENOTSUP) {
274 #else
276 #endif
277 DEBUG(1,("set_ea_dos_attributes: Cannot set attribute EA on file %s: Error = %s\n",
278 path, strerror(errno) ));
279 set_store_dos_attributes(SNUM(conn), False);
281 return False;
284 /* We want DOS semantics, ie allow non owner with write permission to change the
285 bits on a file. Just like file_ntimes below.
288 /* Check if we have write access. */
289 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
290 return False;
293 * We need to open the file with write access whilst
294 * still in our current user context. This ensures we
295 * are not violating security in doing the setxattr.
298 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, path, sbuf,
299 &fsp)))
300 return ret;
301 become_root();
302 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
303 ret = True;
305 unbecome_root();
306 close_file_fchmod(NULL, fsp);
307 return ret;
309 DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
310 return True;
313 /****************************************************************************
314 Change a unix mode to a dos mode for an ms dfs link.
315 ****************************************************************************/
317 uint32 dos_mode_msdfs(connection_struct *conn, const char *path, const SMB_STRUCT_STAT *psbuf)
319 SMB_STRUCT_STAT sbuf = *psbuf;
320 uint32 result = 0;
322 DEBUG(8,("dos_mode_msdfs: %s\n", path));
324 if (!VALID_STAT(sbuf)) {
325 return 0;
328 /* First do any modifications that depend on the path name. */
329 /* hide files with a name starting with a . */
330 if (lp_hide_dot_files(SNUM(conn))) {
331 const char *p = strrchr_m(path,'/');
332 if (p) {
333 p++;
334 } else {
335 p = path;
338 /* Only . and .. are not hidden. */
339 if (p[0] == '.' && !((p[1] == '\0') ||
340 (p[1] == '.' && p[2] == '\0'))) {
341 result |= aHIDDEN;
345 result |= dos_mode_from_sbuf(conn, path, &sbuf);
347 /* Optimization : Only call is_hidden_path if it's not already
348 hidden. */
349 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
350 result |= aHIDDEN;
353 DEBUG(8,("dos_mode_msdfs returning "));
355 if (result & aHIDDEN) DEBUG(8, ("h"));
356 if (result & aRONLY ) DEBUG(8, ("r"));
357 if (result & aSYSTEM) DEBUG(8, ("s"));
358 if (result & aDIR ) DEBUG(8, ("d"));
359 if (result & aARCH ) DEBUG(8, ("a"));
360 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
362 DEBUG(8,("\n"));
364 return(result);
367 #ifdef HAVE_STAT_DOS_FLAGS
368 /****************************************************************************
369 Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
370 ****************************************************************************/
372 int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
374 uint32_t dos_stat_flags = 0;
376 if (dosmode & aARCH)
377 dos_stat_flags |= UF_DOS_ARCHIVE;
378 if (dosmode & aHIDDEN)
379 dos_stat_flags |= UF_DOS_HIDDEN;
380 if (dosmode & aRONLY)
381 dos_stat_flags |= UF_DOS_RO;
382 if (dosmode & aSYSTEM)
383 dos_stat_flags |= UF_DOS_SYSTEM;
384 if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
385 dos_stat_flags |= UF_DOS_NOINDEX;
387 return dos_stat_flags;
390 /****************************************************************************
391 Gets DOS attributes, accessed via st_ex_flags in the stat struct.
392 ****************************************************************************/
394 static bool get_stat_dos_flags(connection_struct *conn,
395 const char *fname,
396 const SMB_STRUCT_STAT *sbuf,
397 uint32_t *dosmode)
399 SMB_ASSERT(sbuf && VALID_STAT(*sbuf));
400 SMB_ASSERT(dosmode);
402 if (!lp_store_dos_attributes(SNUM(conn))) {
403 return false;
406 DEBUG(5, ("Getting stat dos attributes for %s.\n", fname));
408 if (sbuf->st_ex_flags & UF_DOS_ARCHIVE)
409 *dosmode |= aARCH;
410 if (sbuf->st_ex_flags & UF_DOS_HIDDEN)
411 *dosmode |= aHIDDEN;
412 if (sbuf->st_ex_flags & UF_DOS_RO)
413 *dosmode |= aRONLY;
414 if (sbuf->st_ex_flags & UF_DOS_SYSTEM)
415 *dosmode |= aSYSTEM;
416 if (sbuf->st_ex_flags & UF_DOS_NOINDEX)
417 *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
418 if (S_ISDIR(sbuf->st_ex_mode))
419 *dosmode |= aDIR;
421 *dosmode |= set_sparse_flag(sbuf);
422 *dosmode |= set_link_read_only_flag(sbuf);
424 return true;
427 /****************************************************************************
428 Sets DOS attributes, stored in st_ex_flags of the inode.
429 ****************************************************************************/
431 static bool set_stat_dos_flags(connection_struct *conn,
432 const struct smb_filename *smb_fname,
433 uint32_t dosmode,
434 bool *attributes_changed)
436 uint32_t new_flags = 0;
437 int error = 0;
439 SMB_ASSERT(VALID_STAT(smb_fname->st));
440 SMB_ASSERT(attributes_changed);
442 *attributes_changed = false;
444 if (!lp_store_dos_attributes(SNUM(conn))) {
445 return false;
448 DEBUG(5, ("Setting stat dos attributes for %s.\n",
449 smb_fname_str_dbg(smb_fname)));
451 new_flags = (smb_fname->st.st_ex_flags & ~UF_DOS_FLAGS) |
452 dos_attributes_to_stat_dos_flags(dosmode);
454 /* Return early if no flags changed. */
455 if (new_flags == smb_fname->st.st_ex_flags)
456 return true;
458 DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
459 smb_fname->st.st_ex_flags));
461 /* Set new flags with chflags. */
462 error = SMB_VFS_CHFLAGS(conn, smb_fname->base_name, new_flags);
463 if (error) {
464 DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
465 "file %s! errno=%d\n", new_flags,
466 smb_fname_str_dbg(smb_fname), errno));
467 return false;
470 *attributes_changed = true;
471 return true;
473 #endif /* HAVE_STAT_DOS_FLAGS */
475 /****************************************************************************
476 Change a unix mode to a dos mode.
477 ****************************************************************************/
479 uint32 dos_mode(connection_struct *conn, const char *path, const SMB_STRUCT_STAT *psbuf)
481 SMB_STRUCT_STAT sbuf = *psbuf;
482 uint32 result = 0;
483 bool offline, used_stat_dos_flags = false;
485 DEBUG(8,("dos_mode: %s\n", path));
487 if (!VALID_STAT(sbuf)) {
488 return 0;
491 /* First do any modifications that depend on the path name. */
492 /* hide files with a name starting with a . */
493 if (lp_hide_dot_files(SNUM(conn))) {
494 const char *p = strrchr_m(path,'/');
495 if (p) {
496 p++;
497 } else {
498 p = path;
501 /* Only . and .. are not hidden. */
502 if (p[0] == '.' && !((p[1] == '\0') ||
503 (p[1] == '.' && p[2] == '\0'))) {
504 result |= aHIDDEN;
508 #ifdef HAVE_STAT_DOS_FLAGS
509 used_stat_dos_flags = get_stat_dos_flags(conn, path, &sbuf, &result);
510 #endif
511 if (!used_stat_dos_flags) {
512 /* Get the DOS attributes from an EA by preference. */
513 if (get_ea_dos_attribute(conn, path, &sbuf, &result)) {
514 result |= set_sparse_flag(&sbuf);
515 } else {
516 result |= dos_mode_from_sbuf(conn, path, &sbuf);
520 offline = SMB_VFS_IS_OFFLINE(conn, path, &sbuf);
521 if (S_ISREG(sbuf.st_ex_mode) && offline) {
522 result |= FILE_ATTRIBUTE_OFFLINE;
525 /* Optimization : Only call is_hidden_path if it's not already
526 hidden. */
527 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
528 result |= aHIDDEN;
531 DEBUG(8,("dos_mode returning "));
533 if (result & aHIDDEN) DEBUG(8, ("h"));
534 if (result & aRONLY ) DEBUG(8, ("r"));
535 if (result & aSYSTEM) DEBUG(8, ("s"));
536 if (result & aDIR ) DEBUG(8, ("d"));
537 if (result & aARCH ) DEBUG(8, ("a"));
538 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
540 DEBUG(8,("\n"));
542 return(result);
545 /*******************************************************************
546 chmod a file - but preserve some bits.
547 ********************************************************************/
549 int file_set_dosmode(connection_struct *conn, struct smb_filename *smb_fname,
550 uint32 dosmode, const char *parent_dir, bool newfile)
552 int mask=0;
553 mode_t tmp;
554 mode_t unixmode;
555 int ret = -1, lret = -1;
556 uint32_t old_mode;
557 char *fname = NULL;
558 NTSTATUS status;
560 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
561 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
563 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n",
564 dosmode, smb_fname_str_dbg(smb_fname)));
566 if (!VALID_STAT(smb_fname->st)) {
567 if (SMB_VFS_STAT(conn, smb_fname))
568 return(-1);
571 unixmode = smb_fname->st.st_ex_mode;
573 get_acl_group_bits(conn, smb_fname->base_name,
574 &smb_fname->st.st_ex_mode);
576 if (S_ISDIR(smb_fname->st.st_ex_mode))
577 dosmode |= aDIR;
578 else
579 dosmode &= ~aDIR;
581 status = get_full_smb_filename(talloc_tos(), smb_fname, &fname);
582 if (!NT_STATUS_IS_OK(status)) {
583 errno = map_errno_from_nt_status(status);
584 return -1;
587 old_mode = dos_mode(conn, fname, &smb_fname->st);
589 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
590 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
591 lret = SMB_VFS_SET_OFFLINE(conn, fname);
592 if (lret == -1) {
593 DEBUG(0, ("set_dos_mode: client has asked to "
594 "set FILE_ATTRIBUTE_OFFLINE to "
595 "%s/%s but there was an error while "
596 "setting it or it is not "
597 "supported.\n", parent_dir,
598 smb_fname_str_dbg(smb_fname)));
603 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
604 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
606 if (old_mode == dosmode) {
607 smb_fname->st.st_ex_mode = unixmode;
608 return(0);
611 #ifdef HAVE_STAT_DOS_FLAGS
613 bool attributes_changed;
615 if (set_stat_dos_flags(conn, smb_fname, dosmode,
616 &attributes_changed))
618 if (!newfile && attributes_changed) {
619 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
620 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
622 smb_fname->st.st_ex_mode = unixmode;
623 return 0;
626 #endif
628 /* Store the DOS attributes in an EA by preference. */
629 if (set_ea_dos_attribute(conn, fname, &smb_fname->st, dosmode)) {
630 if (!newfile) {
631 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
632 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
634 smb_fname->st.st_ex_mode = unixmode;
635 return 0;
638 unixmode = unix_mode(conn,dosmode,fname, parent_dir);
640 /* preserve the s bits */
641 mask |= (S_ISUID | S_ISGID);
643 /* preserve the t bit */
644 #ifdef S_ISVTX
645 mask |= S_ISVTX;
646 #endif
648 /* possibly preserve the x bits */
649 if (!MAP_ARCHIVE(conn))
650 mask |= S_IXUSR;
651 if (!MAP_SYSTEM(conn))
652 mask |= S_IXGRP;
653 if (!MAP_HIDDEN(conn))
654 mask |= S_IXOTH;
656 unixmode |= (smb_fname->st.st_ex_mode & mask);
658 /* if we previously had any r bits set then leave them alone */
659 if ((tmp = smb_fname->st.st_ex_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
660 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
661 unixmode |= tmp;
664 /* if we previously had any w bits set then leave them alone
665 whilst adding in the new w bits, if the new mode is not rdonly */
666 if (!IS_DOS_READONLY(dosmode)) {
667 unixmode |= (smb_fname->st.st_ex_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
670 ret = SMB_VFS_CHMOD(conn, fname, unixmode);
671 if (ret == 0) {
672 if(!newfile || (lret != -1)) {
673 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
674 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
676 smb_fname->st.st_ex_mode = unixmode;
677 return 0;
680 if((errno != EPERM) && (errno != EACCES))
681 return -1;
683 if(!lp_dos_filemode(SNUM(conn)))
684 return -1;
686 /* We want DOS semantics, ie allow non owner with write permission to change the
687 bits on a file. Just like file_ntimes below.
690 /* Check if we have write access. */
691 if (CAN_WRITE(conn)) {
693 * We need to open the file with write access whilst
694 * still in our current user context. This ensures we
695 * are not violating security in doing the fchmod.
696 * This file open does *not* break any oplocks we are
697 * holding. We need to review this.... may need to
698 * break batch oplocks open by others. JRA.
700 files_struct *fsp;
701 if (!NT_STATUS_IS_OK(open_file_fchmod(NULL, conn, fname,
702 &smb_fname->st, &fsp)))
703 return -1;
704 become_root();
705 ret = SMB_VFS_FCHMOD(fsp, unixmode);
706 unbecome_root();
707 close_file_fchmod(NULL, fsp);
708 if (!newfile) {
709 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
710 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
712 if (ret == 0) {
713 smb_fname->st.st_ex_mode = unixmode;
717 return( ret );
720 /*******************************************************************
721 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
722 than POSIX.
723 *******************************************************************/
725 int file_ntimes(connection_struct *conn, const char *fname,
726 struct smb_file_time *ft, const SMB_STRUCT_STAT *psbuf)
728 struct smb_filename *smb_fname = NULL;
729 NTSTATUS status;
730 int ret = -1;
732 errno = 0;
734 DEBUG(6, ("file_ntime: actime: %s",
735 time_to_asc(convert_timespec_to_time_t(ft->atime))));
736 DEBUG(6, ("file_ntime: modtime: %s",
737 time_to_asc(convert_timespec_to_time_t(ft->mtime))));
738 DEBUG(6, ("file_ntime: createtime: %s",
739 time_to_asc(convert_timespec_to_time_t(ft->create_time))));
741 /* Don't update the time on read-only shares */
742 /* We need this as set_filetime (which can be called on
743 close and other paths) can end up calling this function
744 without the NEED_WRITE protection. Found by :
745 Leo Weppelman <leo@wau.mis.ah.nl>
748 if (!CAN_WRITE(conn)) {
749 return 0;
752 if(SMB_VFS_NTIMES(conn, fname, ft) == 0) {
753 return 0;
756 if((errno != EPERM) && (errno != EACCES)) {
757 return -1;
760 if(!lp_dos_filetimes(SNUM(conn))) {
761 return -1;
764 /* We have permission (given by the Samba admin) to
765 break POSIX semantics and allow a user to change
766 the time on a file they don't own but can write to
767 (as DOS does).
770 status = create_synthetic_smb_fname_split(talloc_tos(), fname, psbuf,
771 &smb_fname);
773 if (!NT_STATUS_IS_OK(status)) {
774 return -1;
777 /* Check if we have write access. */
778 if (can_write_to_file(conn, smb_fname)) {
779 /* We are allowed to become root and change the filetime. */
780 become_root();
781 ret = SMB_VFS_NTIMES(conn, fname, ft);
782 unbecome_root();
784 TALLOC_FREE(smb_fname);
786 return ret;
789 /******************************************************************
790 Force a "sticky" write time on a pathname. This will always be
791 returned on all future write time queries and set on close.
792 ******************************************************************/
794 bool set_sticky_write_time_path(connection_struct *conn, const char *fname,
795 struct file_id fileid, const struct timespec mtime)
797 if (null_timespec(mtime)) {
798 return true;
801 if (!set_sticky_write_time(fileid, mtime)) {
802 return false;
805 return true;
808 /******************************************************************
809 Force a "sticky" write time on an fsp. This will always be
810 returned on all future write time queries and set on close.
811 ******************************************************************/
813 bool set_sticky_write_time_fsp(struct files_struct *fsp, const struct timespec mtime)
815 fsp->write_time_forced = true;
816 TALLOC_FREE(fsp->update_write_time_event);
818 return set_sticky_write_time_path(fsp->conn, fsp->fsp_name,
819 fsp->file_id, mtime);
822 /******************************************************************
823 Update a write time immediately, without the 2 second delay.
824 ******************************************************************/
826 bool update_write_time(struct files_struct *fsp)
828 if (!set_write_time(fsp->file_id, timespec_current())) {
829 return false;
832 notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
833 FILE_NOTIFY_CHANGE_LAST_WRITE, fsp->fsp_name);
835 return true;