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 2 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, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
26 #if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
27 if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
28 return FILE_ATTRIBUTE_SPARSE;
34 /****************************************************************************
35 Work out whether this file is offline
36 ****************************************************************************/
39 #define ISDOT(p) (*(p) == '.' && *((p) + 1) == '\0')
43 #define ISDOTDOT(p) (*(p) == '.' && *((p) + 1) == '.' && *((p) + 2) == '\0')
46 static uint32 set_offline_flag(connection_struct *conn, const char *const path)
48 if (ISDOT(path) || ISDOTDOT(path)) {
52 if (!lp_dmapi_support(SNUM(conn)) || !dmapi_have_session()) {
56 return dmapi_file_flags(path);
59 /****************************************************************************
60 Change a dos mode to a unix mode.
61 Base permission for files:
62 if creating file and inheriting (i.e. parent_dir != NULL)
63 apply read/write bits from parent directory.
65 everybody gets read bit set
66 dos readonly is represented in unix by removing everyone's write bit
67 dos archive is represented in unix by the user's execute bit
68 dos system is represented in unix by the group's execute bit
69 dos hidden is represented in unix by the other's execute bit
71 Then apply create mask,
74 Base permission for directories:
75 dos directory is represented in unix by unix's dir bit and the exec bit
77 Then apply create mask,
80 ****************************************************************************/
82 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
83 const char *inherit_from_dir)
85 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
86 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
89 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
90 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
93 if (fname && (inherit_from_dir != NULL)
94 && lp_inherit_perms(SNUM(conn))) {
97 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
99 if (SMB_VFS_STAT(conn, inherit_from_dir, &sbuf) != 0) {
100 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname,
101 inherit_from_dir, strerror(errno)));
102 return(0); /* *** shouldn't happen! *** */
105 /* Save for later - but explicitly remove setuid bit for safety. */
106 dir_mode = sbuf.st_mode & ~S_ISUID;
107 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
112 if (IS_DOS_DIR(dosmode)) {
113 /* We never make directories read only for the owner as under DOS a user
114 can always create a file in a read-only directory. */
115 result |= (S_IFDIR | S_IWUSR);
118 /* Inherit mode of parent directory. */
121 /* Provisionally add all 'x' bits */
122 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
124 /* Apply directory mask */
125 result &= lp_dir_mask(SNUM(conn));
126 /* Add in force bits */
127 result |= lp_force_dir_mode(SNUM(conn));
130 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
133 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
136 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
140 /* Inherit 666 component of parent directory mode */
141 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
143 /* Apply mode mask */
144 result &= lp_create_mask(SNUM(conn));
145 /* Add in force bits */
146 result |= lp_force_create_mode(SNUM(conn));
150 DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
154 /****************************************************************************
155 Change a unix mode to a dos mode.
156 ****************************************************************************/
158 static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
161 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
163 if (ro_opts == MAP_READONLY_YES) {
164 /* Original Samba method - map inverse of user "w" bit. */
165 if ((sbuf->st_mode & S_IWUSR) == 0) {
168 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
169 /* Check actual permissions for read-only. */
170 if (!can_write_to_file(conn, path, sbuf)) {
173 } /* Else never set the readonly bit. */
175 if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
178 if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
181 if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
184 if (S_ISDIR(sbuf->st_mode))
185 result = aDIR | (result & aRONLY);
187 result |= set_sparse_flag(sbuf);
191 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
196 DEBUG(8,("dos_mode_from_sbuf returning "));
198 if (result & aHIDDEN) DEBUG(8, ("h"));
199 if (result & aRONLY ) DEBUG(8, ("r"));
200 if (result & aSYSTEM) DEBUG(8, ("s"));
201 if (result & aDIR ) DEBUG(8, ("d"));
202 if (result & aARCH ) DEBUG(8, ("a"));
208 /****************************************************************************
209 Get DOS attributes from an EA.
210 ****************************************************************************/
212 static BOOL get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
216 unsigned int dosattr;
218 if (!lp_store_dos_attributes(SNUM(conn))) {
222 /* Don't reset pattr to zero as we may already have filename-based attributes we
225 sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
227 #if defined(ENOTSUP) && defined(ENOATTR)
228 if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES) && (errno != EPERM)) {
229 DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
230 path, strerror(errno) ));
231 set_store_dos_attributes(SNUM(conn), False);
236 /* Null terminate string. */
237 attrstr[sizeret] = 0;
238 DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
240 if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
241 sscanf(attrstr, "%x", &dosattr) != 1) {
242 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
246 if (S_ISDIR(sbuf->st_mode)) {
249 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
251 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
253 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
254 if (dosattr & aRONLY ) DEBUG(8, ("r"));
255 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
256 if (dosattr & aDIR ) DEBUG(8, ("d"));
257 if (dosattr & aARCH ) DEBUG(8, ("a"));
264 /****************************************************************************
265 Set DOS attributes in an EA.
266 ****************************************************************************/
268 static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
271 files_struct *fsp = NULL;
274 if (!lp_store_dos_attributes(SNUM(conn))) {
278 snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
279 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
280 if((errno != EPERM) && (errno != EACCES)) {
283 || errno == ENOTSUP) {
287 set_store_dos_attributes(SNUM(conn), False);
292 /* We want DOS semantics, ie allow non owner with write permission to change the
293 bits on a file. Just like file_utime below.
296 /* Check if we have write access. */
297 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
301 * We need to open the file with write access whilst
302 * still in our current user context. This ensures we
303 * are not violating security in doing the setxattr.
306 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,path,sbuf,&fsp)))
309 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
313 close_file_fchmod(fsp);
316 DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
320 /****************************************************************************
321 Change a unix mode to a dos mode for an ms dfs link.
322 ****************************************************************************/
324 uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
328 DEBUG(8,("dos_mode_msdfs: %s\n", path));
330 if (!VALID_STAT(*sbuf)) {
334 /* First do any modifications that depend on the path name. */
335 /* hide files with a name starting with a . */
336 if (lp_hide_dot_files(SNUM(conn))) {
337 const char *p = strrchr_m(path,'/');
344 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
349 result |= dos_mode_from_sbuf(conn, path, sbuf);
351 /* Optimization : Only call is_hidden_path if it's not already
353 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
357 DEBUG(8,("dos_mode_msdfs returning "));
359 if (result & aHIDDEN) DEBUG(8, ("h"));
360 if (result & aRONLY ) DEBUG(8, ("r"));
361 if (result & aSYSTEM) DEBUG(8, ("s"));
362 if (result & aDIR ) DEBUG(8, ("d"));
363 if (result & aARCH ) DEBUG(8, ("a"));
364 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
371 /****************************************************************************
372 Change a unix mode to a dos mode.
373 ****************************************************************************/
375 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
379 DEBUG(8,("dos_mode: %s\n", path));
381 if (!VALID_STAT(*sbuf)) {
385 /* First do any modifications that depend on the path name. */
386 /* hide files with a name starting with a . */
387 if (lp_hide_dot_files(SNUM(conn))) {
388 const char *p = strrchr_m(path,'/');
395 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
400 /* Get the DOS attributes from an EA by preference. */
401 if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
402 result |= set_sparse_flag(sbuf);
404 result |= dos_mode_from_sbuf(conn, path, sbuf);
407 if (S_ISREG(sbuf->st_mode)) {
408 result |= set_offline_flag(conn, path);
411 /* Optimization : Only call is_hidden_path if it's not already
413 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
417 DEBUG(8,("dos_mode returning "));
419 if (result & aHIDDEN) DEBUG(8, ("h"));
420 if (result & aRONLY ) DEBUG(8, ("r"));
421 if (result & aSYSTEM) DEBUG(8, ("s"));
422 if (result & aDIR ) DEBUG(8, ("d"));
423 if (result & aARCH ) DEBUG(8, ("a"));
424 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
431 /*******************************************************************
432 chmod a file - but preserve some bits.
433 ********************************************************************/
435 int file_set_dosmode(connection_struct *conn, const char *fname,
436 uint32 dosmode, SMB_STRUCT_STAT *st,
437 const char *parent_dir)
445 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
446 dosmode &= SAMBA_ATTRIBUTES_MASK;
448 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
449 if (!st || (st && !VALID_STAT(*st))) {
451 if (SMB_VFS_STAT(conn,fname,st))
455 get_acl_group_bits(conn, fname, &st->st_mode);
457 if (S_ISDIR(st->st_mode))
462 if (dos_mode(conn,fname,st) == dosmode)
465 /* Store the DOS attributes in an EA by preference. */
466 if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
470 unixmode = unix_mode(conn,dosmode,fname, parent_dir);
472 /* preserve the s bits */
473 mask |= (S_ISUID | S_ISGID);
475 /* preserve the t bit */
480 /* possibly preserve the x bits */
481 if (!MAP_ARCHIVE(conn))
483 if (!MAP_SYSTEM(conn))
485 if (!MAP_HIDDEN(conn))
488 unixmode |= (st->st_mode & mask);
490 /* if we previously had any r bits set then leave them alone */
491 if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
492 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
496 /* if we previously had any w bits set then leave them alone
497 whilst adding in the new w bits, if the new mode is not rdonly */
498 if (!IS_DOS_READONLY(dosmode)) {
499 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
502 if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0)
505 if((errno != EPERM) && (errno != EACCES))
508 if(!lp_dos_filemode(SNUM(conn)))
511 /* We want DOS semantics, ie allow non owner with write permission to change the
512 bits on a file. Just like file_utime below.
515 /* Check if we have write access. */
516 if (CAN_WRITE(conn)) {
518 * We need to open the file with write access whilst
519 * still in our current user context. This ensures we
520 * are not violating security in doing the fchmod.
521 * This file open does *not* break any oplocks we are
522 * holding. We need to review this.... may need to
523 * break batch oplocks open by others. JRA.
526 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
529 ret = SMB_VFS_FCHMOD(fsp, fsp->fh->fd, unixmode);
531 close_file_fchmod(fsp);
537 /*******************************************************************
538 Wrapper around dos_utime that possibly allows DOS semantics rather
540 *******************************************************************/
542 int file_utime(connection_struct *conn, const char *fname, struct utimbuf *times)
544 SMB_STRUCT_STAT sbuf;
550 /* Don't update the time on read-only shares */
551 /* We need this as set_filetime (which can be called on
552 close and other paths) can end up calling this function
553 without the NEED_WRITE protection. Found by :
554 Leo Weppelman <leo@wau.mis.ah.nl>
557 if (!CAN_WRITE(conn)) {
561 if(SMB_VFS_UTIME(conn,fname, times) == 0)
564 if((errno != EPERM) && (errno != EACCES))
567 if(!lp_dos_filetimes(SNUM(conn)))
570 /* We have permission (given by the Samba admin) to
571 break POSIX semantics and allow a user to change
572 the time on a file they don't own but can write to
576 /* Check if we have write access. */
577 if (can_write_to_file(conn, fname, &sbuf)) {
578 /* We are allowed to become root and change the filetime. */
580 ret = SMB_VFS_UTIME(conn,fname, times);
587 /*******************************************************************
588 Change a filetime - possibly allowing DOS semantics.
589 *******************************************************************/
591 BOOL set_filetime(connection_struct *conn, const char *fname, time_t mtime)
593 struct utimbuf times;
595 if (null_mtime(mtime))
598 times.modtime = times.actime = mtime;
600 if (file_utime(conn, fname, ×)) {
601 DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));