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/>.
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_size > sbuf->st_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
27 return FILE_ATTRIBUTE_SPARSE;
33 /****************************************************************************
34 Change a dos mode to a unix mode.
35 Base permission for files:
36 if creating file and inheriting (i.e. parent_dir != NULL)
37 apply read/write bits from parent directory.
39 everybody gets read bit set
40 dos readonly is represented in unix by removing everyone's write bit
41 dos archive is represented in unix by the user's execute bit
42 dos system is represented in unix by the group's execute bit
43 dos hidden is represented in unix by the other's execute bit
45 Then apply create mask,
48 Base permission for directories:
49 dos directory is represented in unix by unix's dir bit and the exec bit
51 Then apply create mask,
54 ****************************************************************************/
56 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
57 const char *inherit_from_dir)
59 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
60 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
63 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
64 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
67 if (fname && (inherit_from_dir != NULL)
68 && lp_inherit_perms(SNUM(conn))) {
71 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
73 if (SMB_VFS_STAT(conn, inherit_from_dir, &sbuf) != 0) {
74 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname,
75 inherit_from_dir, strerror(errno)));
76 return(0); /* *** shouldn't happen! *** */
79 /* Save for later - but explicitly remove setuid bit for safety. */
80 dir_mode = sbuf.st_mode & ~S_ISUID;
81 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
86 if (IS_DOS_DIR(dosmode)) {
87 /* We never make directories read only for the owner as under DOS a user
88 can always create a file in a read-only directory. */
89 result |= (S_IFDIR | S_IWUSR);
92 /* Inherit mode of parent directory. */
95 /* Provisionally add all 'x' bits */
96 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
98 /* Apply directory mask */
99 result &= lp_dir_mask(SNUM(conn));
100 /* Add in force bits */
101 result |= lp_force_dir_mode(SNUM(conn));
104 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
107 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
110 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
114 /* Inherit 666 component of parent directory mode */
115 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
117 /* Apply mode mask */
118 result &= lp_create_mask(SNUM(conn));
119 /* Add in force bits */
120 result |= lp_force_create_mode(SNUM(conn));
124 DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
128 /****************************************************************************
129 Change a unix mode to a dos mode.
130 ****************************************************************************/
132 static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
135 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
137 if (ro_opts == MAP_READONLY_YES) {
138 /* Original Samba method - map inverse of user "w" bit. */
139 if ((sbuf->st_mode & S_IWUSR) == 0) {
142 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
143 /* Check actual permissions for read-only. */
144 if (!can_write_to_file(conn, path, sbuf)) {
147 } /* Else never set the readonly bit. */
149 if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
152 if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
155 if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
158 if (S_ISDIR(sbuf->st_mode))
159 result = aDIR | (result & aRONLY);
161 result |= set_sparse_flag(sbuf);
165 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
170 DEBUG(8,("dos_mode_from_sbuf returning "));
172 if (result & aHIDDEN) DEBUG(8, ("h"));
173 if (result & aRONLY ) DEBUG(8, ("r"));
174 if (result & aSYSTEM) DEBUG(8, ("s"));
175 if (result & aDIR ) DEBUG(8, ("d"));
176 if (result & aARCH ) DEBUG(8, ("a"));
182 /****************************************************************************
183 Get DOS attributes from an EA.
184 ****************************************************************************/
186 static bool get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
190 unsigned int dosattr;
192 if (!lp_store_dos_attributes(SNUM(conn))) {
196 /* Don't reset pattr to zero as we may already have filename-based attributes we
199 sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
201 #if defined(ENOTSUP) && defined(ENOATTR)
202 if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES) && (errno != EPERM)) {
203 DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
204 path, strerror(errno) ));
205 set_store_dos_attributes(SNUM(conn), False);
210 /* Null terminate string. */
211 attrstr[sizeret] = 0;
212 DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
214 if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
215 sscanf(attrstr, "%x", &dosattr) != 1) {
216 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
220 if (S_ISDIR(sbuf->st_mode)) {
223 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
225 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
227 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
228 if (dosattr & aRONLY ) DEBUG(8, ("r"));
229 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
230 if (dosattr & aDIR ) DEBUG(8, ("d"));
231 if (dosattr & aARCH ) DEBUG(8, ("a"));
238 /****************************************************************************
239 Set DOS attributes in an EA.
240 ****************************************************************************/
242 static bool set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
245 files_struct *fsp = NULL;
248 if (!lp_store_dos_attributes(SNUM(conn))) {
252 snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
253 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
254 if((errno != EPERM) && (errno != EACCES)) {
257 || errno == ENOTSUP) {
261 set_store_dos_attributes(SNUM(conn), False);
266 /* We want DOS semantics, ie allow non owner with write permission to change the
267 bits on a file. Just like file_ntimes below.
270 /* Check if we have write access. */
271 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
275 * We need to open the file with write access whilst
276 * still in our current user context. This ensures we
277 * are not violating security in doing the setxattr.
280 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,path,sbuf,&fsp)))
283 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
287 close_file_fchmod(fsp);
290 DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
294 /****************************************************************************
295 Change a unix mode to a dos mode for an ms dfs link.
296 ****************************************************************************/
298 uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
302 DEBUG(8,("dos_mode_msdfs: %s\n", path));
304 if (!VALID_STAT(*sbuf)) {
308 /* First do any modifications that depend on the path name. */
309 /* hide files with a name starting with a . */
310 if (lp_hide_dot_files(SNUM(conn))) {
311 const char *p = strrchr_m(path,'/');
318 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
323 result |= dos_mode_from_sbuf(conn, path, sbuf);
325 /* Optimization : Only call is_hidden_path if it's not already
327 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
331 DEBUG(8,("dos_mode_msdfs returning "));
333 if (result & aHIDDEN) DEBUG(8, ("h"));
334 if (result & aRONLY ) DEBUG(8, ("r"));
335 if (result & aSYSTEM) DEBUG(8, ("s"));
336 if (result & aDIR ) DEBUG(8, ("d"));
337 if (result & aARCH ) DEBUG(8, ("a"));
338 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
345 /****************************************************************************
346 Change a unix mode to a dos mode.
347 ****************************************************************************/
349 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
355 DEBUG(8,("dos_mode: %s\n", path));
357 if (!VALID_STAT(*sbuf)) {
361 /* First do any modifications that depend on the path name. */
362 /* hide files with a name starting with a . */
363 if (lp_hide_dot_files(SNUM(conn))) {
364 const char *p = strrchr_m(path,'/');
371 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
376 /* Get the DOS attributes from an EA by preference. */
377 if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
378 result |= set_sparse_flag(sbuf);
380 result |= dos_mode_from_sbuf(conn, path, sbuf);
384 ret = SMB_VFS_IS_OFFLINE(conn, path, sbuf, &offline);
385 if (S_ISREG(sbuf->st_mode) && (ret == 0) && offline) {
386 result |= FILE_ATTRIBUTE_OFFLINE;
389 /* Optimization : Only call is_hidden_path if it's not already
391 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
395 DEBUG(8,("dos_mode returning "));
397 if (result & aHIDDEN) DEBUG(8, ("h"));
398 if (result & aRONLY ) DEBUG(8, ("r"));
399 if (result & aSYSTEM) DEBUG(8, ("s"));
400 if (result & aDIR ) DEBUG(8, ("d"));
401 if (result & aARCH ) DEBUG(8, ("a"));
402 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
409 /*******************************************************************
410 chmod a file - but preserve some bits.
411 ********************************************************************/
413 int file_set_dosmode(connection_struct *conn, const char *fname,
414 uint32 dosmode, SMB_STRUCT_STAT *st,
415 const char *parent_dir,
422 int ret = -1, lret = -1;
424 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
425 dosmode &= SAMBA_ATTRIBUTES_MASK;
427 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
430 SET_STAT_INVALID(st1);
434 if (!VALID_STAT(*st)) {
435 if (SMB_VFS_STAT(conn,fname,st))
439 unixmode = st->st_mode;
441 get_acl_group_bits(conn, fname, &st->st_mode);
443 if (S_ISDIR(st->st_mode))
448 if (dos_mode(conn,fname,st) == dosmode) {
449 st->st_mode = unixmode;
453 /* Store the DOS attributes in an EA by preference. */
454 if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
456 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
457 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
459 st->st_mode = unixmode;
463 unixmode = unix_mode(conn,dosmode,fname, parent_dir);
465 /* preserve the s bits */
466 mask |= (S_ISUID | S_ISGID);
468 /* preserve the t bit */
473 /* possibly preserve the x bits */
474 if (!MAP_ARCHIVE(conn))
476 if (!MAP_SYSTEM(conn))
478 if (!MAP_HIDDEN(conn))
481 unixmode |= (st->st_mode & mask);
483 /* if we previously had any r bits set then leave them alone */
484 if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
485 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
489 /* if we previously had any w bits set then leave them alone
490 whilst adding in the new w bits, if the new mode is not rdonly */
491 if (!IS_DOS_READONLY(dosmode)) {
492 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
495 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
496 lret = SMB_VFS_SET_OFFLINE(conn, fname);
498 DEBUG(0, ("set_dos_mode: client has asked to set "
499 "FILE_ATTRIBUTE_OFFLINE to %s/%s but there was "
500 "an error while setting it or it is not supported.\n",
505 ret = SMB_VFS_CHMOD(conn, fname, unixmode);
507 if(!newfile || (lret != -1)) {
508 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
509 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
511 st->st_mode = unixmode;
515 if((errno != EPERM) && (errno != EACCES))
518 if(!lp_dos_filemode(SNUM(conn)))
521 /* We want DOS semantics, ie allow non owner with write permission to change the
522 bits on a file. Just like file_ntimes below.
525 /* Check if we have write access. */
526 if (CAN_WRITE(conn)) {
528 * We need to open the file with write access whilst
529 * still in our current user context. This ensures we
530 * are not violating security in doing the fchmod.
531 * This file open does *not* break any oplocks we are
532 * holding. We need to review this.... may need to
533 * break batch oplocks open by others. JRA.
536 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
539 ret = SMB_VFS_FCHMOD(fsp, unixmode);
541 close_file_fchmod(fsp);
543 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
544 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
547 st->st_mode = unixmode;
554 /*******************************************************************
555 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
557 *******************************************************************/
559 int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2])
561 SMB_STRUCT_STAT sbuf;
567 /* Don't update the time on read-only shares */
568 /* We need this as set_filetime (which can be called on
569 close and other paths) can end up calling this function
570 without the NEED_WRITE protection. Found by :
571 Leo Weppelman <leo@wau.mis.ah.nl>
574 if (!CAN_WRITE(conn)) {
578 if(SMB_VFS_NTIMES(conn, fname, ts) == 0) {
582 if((errno != EPERM) && (errno != EACCES)) {
586 if(!lp_dos_filetimes(SNUM(conn))) {
590 /* We have permission (given by the Samba admin) to
591 break POSIX semantics and allow a user to change
592 the time on a file they don't own but can write to
596 /* Check if we have write access. */
597 if (can_write_to_file(conn, fname, &sbuf)) {
598 /* We are allowed to become root and change the filetime. */
600 ret = SMB_VFS_NTIMES(conn, fname, ts);
607 /*******************************************************************
608 Change a filetime - possibly allowing DOS semantics.
609 *******************************************************************/
611 bool set_filetime(connection_struct *conn, const char *fname,
612 const struct timespec mtime)
614 struct timespec ts[2];
616 if (null_timespec(mtime)) {
620 ts[1] = mtime; /* mtime. */
621 ts[0] = ts[1]; /* atime. */
623 if (file_ntimes(conn, fname, ts)) {
624 DEBUG(4,("set_filetime(%s) failed: %s\n",
625 fname,strerror(errno)));
629 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
630 FILE_NOTIFY_CHANGE_LAST_WRITE, fname);