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 Work out whether this file is offline
35 ****************************************************************************/
37 static uint32 set_offline_flag(connection_struct *conn, const char *const path)
39 if (ISDOT(path) || ISDOTDOT(path)) {
43 if (!lp_dmapi_support(SNUM(conn)) || !dmapi_have_session()) {
47 return dmapi_file_flags(path);
50 /****************************************************************************
51 Change a dos mode to a unix mode.
52 Base permission for files:
53 if creating file and inheriting (i.e. parent_dir != NULL)
54 apply read/write bits from parent directory.
56 everybody gets read bit set
57 dos readonly is represented in unix by removing everyone's write bit
58 dos archive is represented in unix by the user's execute bit
59 dos system is represented in unix by the group's execute bit
60 dos hidden is represented in unix by the other's execute bit
62 Then apply create mask,
65 Base permission for directories:
66 dos directory is represented in unix by unix's dir bit and the exec bit
68 Then apply create mask,
71 ****************************************************************************/
73 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
74 const char *inherit_from_dir)
76 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
77 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
80 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
81 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
84 if (fname && (inherit_from_dir != NULL)
85 && lp_inherit_perms(SNUM(conn))) {
88 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
90 if (SMB_VFS_STAT(conn, inherit_from_dir, &sbuf) != 0) {
91 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname,
92 inherit_from_dir, strerror(errno)));
93 return(0); /* *** shouldn't happen! *** */
96 /* Save for later - but explicitly remove setuid bit for safety. */
97 dir_mode = sbuf.st_mode & ~S_ISUID;
98 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
103 if (IS_DOS_DIR(dosmode)) {
104 /* We never make directories read only for the owner as under DOS a user
105 can always create a file in a read-only directory. */
106 result |= (S_IFDIR | S_IWUSR);
109 /* Inherit mode of parent directory. */
112 /* Provisionally add all 'x' bits */
113 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
115 /* Apply directory mask */
116 result &= lp_dir_mask(SNUM(conn));
117 /* Add in force bits */
118 result |= lp_force_dir_mode(SNUM(conn));
121 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
124 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
127 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
131 /* Inherit 666 component of parent directory mode */
132 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
134 /* Apply mode mask */
135 result &= lp_create_mask(SNUM(conn));
136 /* Add in force bits */
137 result |= lp_force_create_mode(SNUM(conn));
141 DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
145 /****************************************************************************
146 Change a unix mode to a dos mode.
147 ****************************************************************************/
149 static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
152 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
154 if (ro_opts == MAP_READONLY_YES) {
155 /* Original Samba method - map inverse of user "w" bit. */
156 if ((sbuf->st_mode & S_IWUSR) == 0) {
159 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
160 /* Check actual permissions for read-only. */
161 if (!can_write_to_file(conn, path, sbuf)) {
164 } /* Else never set the readonly bit. */
166 if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
169 if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
172 if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
175 if (S_ISDIR(sbuf->st_mode))
176 result = aDIR | (result & aRONLY);
178 result |= set_sparse_flag(sbuf);
182 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
187 DEBUG(8,("dos_mode_from_sbuf returning "));
189 if (result & aHIDDEN) DEBUG(8, ("h"));
190 if (result & aRONLY ) DEBUG(8, ("r"));
191 if (result & aSYSTEM) DEBUG(8, ("s"));
192 if (result & aDIR ) DEBUG(8, ("d"));
193 if (result & aARCH ) DEBUG(8, ("a"));
199 /****************************************************************************
200 Get DOS attributes from an EA.
201 ****************************************************************************/
203 static bool get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
207 unsigned int dosattr;
209 if (!lp_store_dos_attributes(SNUM(conn))) {
213 /* Don't reset pattr to zero as we may already have filename-based attributes we
216 sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
218 #if defined(ENOTSUP) && defined(ENOATTR)
219 if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES) && (errno != EPERM)) {
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);
227 /* Null terminate string. */
228 attrstr[sizeret] = 0;
229 DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
231 if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
232 sscanf(attrstr, "%x", &dosattr) != 1) {
233 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
237 if (S_ISDIR(sbuf->st_mode)) {
240 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
242 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
244 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
245 if (dosattr & aRONLY ) DEBUG(8, ("r"));
246 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
247 if (dosattr & aDIR ) DEBUG(8, ("d"));
248 if (dosattr & aARCH ) DEBUG(8, ("a"));
255 /****************************************************************************
256 Set DOS attributes in an EA.
257 ****************************************************************************/
259 static bool set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
262 files_struct *fsp = NULL;
265 if (!lp_store_dos_attributes(SNUM(conn))) {
269 snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
270 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
271 if((errno != EPERM) && (errno != EACCES)) {
274 || errno == ENOTSUP) {
278 set_store_dos_attributes(SNUM(conn), False);
283 /* We want DOS semantics, ie allow non owner with write permission to change the
284 bits on a file. Just like file_ntimes below.
287 /* Check if we have write access. */
288 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
292 * We need to open the file with write access whilst
293 * still in our current user context. This ensures we
294 * are not violating security in doing the setxattr.
297 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,path,sbuf,&fsp)))
300 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
304 close_file_fchmod(fsp);
307 DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
311 /****************************************************************************
312 Change a unix mode to a dos mode for an ms dfs link.
313 ****************************************************************************/
315 uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
319 DEBUG(8,("dos_mode_msdfs: %s\n", path));
321 if (!VALID_STAT(*sbuf)) {
325 /* First do any modifications that depend on the path name. */
326 /* hide files with a name starting with a . */
327 if (lp_hide_dot_files(SNUM(conn))) {
328 const char *p = strrchr_m(path,'/');
335 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
340 result |= dos_mode_from_sbuf(conn, path, sbuf);
342 /* Optimization : Only call is_hidden_path if it's not already
344 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
348 DEBUG(8,("dos_mode_msdfs returning "));
350 if (result & aHIDDEN) DEBUG(8, ("h"));
351 if (result & aRONLY ) DEBUG(8, ("r"));
352 if (result & aSYSTEM) DEBUG(8, ("s"));
353 if (result & aDIR ) DEBUG(8, ("d"));
354 if (result & aARCH ) DEBUG(8, ("a"));
355 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
362 /****************************************************************************
363 Change a unix mode to a dos mode.
364 ****************************************************************************/
366 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
370 DEBUG(8,("dos_mode: %s\n", path));
372 if (!VALID_STAT(*sbuf)) {
376 /* First do any modifications that depend on the path name. */
377 /* hide files with a name starting with a . */
378 if (lp_hide_dot_files(SNUM(conn))) {
379 const char *p = strrchr_m(path,'/');
386 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
391 /* Get the DOS attributes from an EA by preference. */
392 if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
393 result |= set_sparse_flag(sbuf);
395 result |= dos_mode_from_sbuf(conn, path, sbuf);
398 if (S_ISREG(sbuf->st_mode)) {
399 result |= set_offline_flag(conn, path);
402 /* Optimization : Only call is_hidden_path if it's not already
404 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
408 DEBUG(8,("dos_mode returning "));
410 if (result & aHIDDEN) DEBUG(8, ("h"));
411 if (result & aRONLY ) DEBUG(8, ("r"));
412 if (result & aSYSTEM) DEBUG(8, ("s"));
413 if (result & aDIR ) DEBUG(8, ("d"));
414 if (result & aARCH ) DEBUG(8, ("a"));
415 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
422 /*******************************************************************
423 chmod a file - but preserve some bits.
424 ********************************************************************/
426 int file_set_dosmode(connection_struct *conn, const char *fname,
427 uint32 dosmode, SMB_STRUCT_STAT *st,
428 const char *parent_dir,
437 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
438 dosmode &= SAMBA_ATTRIBUTES_MASK;
440 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
443 SET_STAT_INVALID(st1);
447 if (!VALID_STAT(*st)) {
448 if (SMB_VFS_STAT(conn,fname,st))
452 unixmode = st->st_mode;
454 get_acl_group_bits(conn, fname, &st->st_mode);
456 if (S_ISDIR(st->st_mode))
461 if (dos_mode(conn,fname,st) == dosmode) {
462 st->st_mode = unixmode;
466 /* Store the DOS attributes in an EA by preference. */
467 if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
469 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
470 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
472 st->st_mode = unixmode;
476 unixmode = unix_mode(conn,dosmode,fname, parent_dir);
478 /* preserve the s bits */
479 mask |= (S_ISUID | S_ISGID);
481 /* preserve the t bit */
486 /* possibly preserve the x bits */
487 if (!MAP_ARCHIVE(conn))
489 if (!MAP_SYSTEM(conn))
491 if (!MAP_HIDDEN(conn))
494 unixmode |= (st->st_mode & mask);
496 /* if we previously had any r bits set then leave them alone */
497 if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
498 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
502 /* if we previously had any w bits set then leave them alone
503 whilst adding in the new w bits, if the new mode is not rdonly */
504 if (!IS_DOS_READONLY(dosmode)) {
505 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
508 if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0) {
510 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
511 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
513 st->st_mode = unixmode;
517 if((errno != EPERM) && (errno != EACCES))
520 if(!lp_dos_filemode(SNUM(conn)))
523 /* We want DOS semantics, ie allow non owner with write permission to change the
524 bits on a file. Just like file_ntimes below.
527 /* Check if we have write access. */
528 if (CAN_WRITE(conn)) {
530 * We need to open the file with write access whilst
531 * still in our current user context. This ensures we
532 * are not violating security in doing the fchmod.
533 * This file open does *not* break any oplocks we are
534 * holding. We need to review this.... may need to
535 * break batch oplocks open by others. JRA.
538 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
541 ret = SMB_VFS_FCHMOD(fsp, unixmode);
543 close_file_fchmod(fsp);
545 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
546 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
549 st->st_mode = unixmode;
556 /*******************************************************************
557 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
559 *******************************************************************/
561 int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2])
563 SMB_STRUCT_STAT sbuf;
569 /* Don't update the time on read-only shares */
570 /* We need this as set_filetime (which can be called on
571 close and other paths) can end up calling this function
572 without the NEED_WRITE protection. Found by :
573 Leo Weppelman <leo@wau.mis.ah.nl>
576 if (!CAN_WRITE(conn)) {
580 if(SMB_VFS_NTIMES(conn, fname, ts) == 0) {
584 if((errno != EPERM) && (errno != EACCES)) {
588 if(!lp_dos_filetimes(SNUM(conn))) {
592 /* We have permission (given by the Samba admin) to
593 break POSIX semantics and allow a user to change
594 the time on a file they don't own but can write to
598 /* Check if we have write access. */
599 if (can_write_to_file(conn, fname, &sbuf)) {
600 /* We are allowed to become root and change the filetime. */
602 ret = SMB_VFS_NTIMES(conn, fname, ts);
609 /*******************************************************************
610 Change a filetime - possibly allowing DOS semantics.
611 *******************************************************************/
613 bool set_filetime(connection_struct *conn, const char *fname,
614 const struct timespec mtime)
616 struct timespec ts[2];
618 if (null_timespec(mtime)) {
622 ts[1] = mtime; /* mtime. */
623 ts[0] = ts[1]; /* atime. */
625 if (file_ntimes(conn, fname, ts)) {
626 DEBUG(4,("set_filetime(%s) failed: %s\n",
627 fname,strerror(errno)));
631 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
632 FILE_NOTIFY_CHANGE_LAST_WRITE, fname);