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
)
436 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
437 dosmode
&= SAMBA_ATTRIBUTES_MASK
;
439 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode
, fname
));
440 if (!st
|| (st
&& !VALID_STAT(*st
))) {
442 if (SMB_VFS_STAT(conn
,fname
,st
))
446 get_acl_group_bits(conn
, fname
, &st
->st_mode
);
448 if (S_ISDIR(st
->st_mode
))
453 if (dos_mode(conn
,fname
,st
) == dosmode
)
456 /* Store the DOS attributes in an EA by preference. */
457 if (set_ea_dos_attribute(conn
, fname
, st
, dosmode
)) {
461 unixmode
= unix_mode(conn
,dosmode
,fname
, parent_dir
);
463 /* preserve the s bits */
464 mask
|= (S_ISUID
| S_ISGID
);
466 /* preserve the t bit */
471 /* possibly preserve the x bits */
472 if (!MAP_ARCHIVE(conn
))
474 if (!MAP_SYSTEM(conn
))
476 if (!MAP_HIDDEN(conn
))
479 unixmode
|= (st
->st_mode
& mask
);
481 /* if we previously had any r bits set then leave them alone */
482 if ((tmp
= st
->st_mode
& (S_IRUSR
|S_IRGRP
|S_IROTH
))) {
483 unixmode
&= ~(S_IRUSR
|S_IRGRP
|S_IROTH
);
487 /* if we previously had any w bits set then leave them alone
488 whilst adding in the new w bits, if the new mode is not rdonly */
489 if (!IS_DOS_READONLY(dosmode
)) {
490 unixmode
|= (st
->st_mode
& (S_IWUSR
|S_IWGRP
|S_IWOTH
));
493 if ((ret
= SMB_VFS_CHMOD(conn
,fname
,unixmode
)) == 0) {
494 notify_fname(conn
, NOTIFY_ACTION_MODIFIED
,
495 FILE_NOTIFY_CHANGE_ATTRIBUTES
, fname
);
499 if((errno
!= EPERM
) && (errno
!= EACCES
))
502 if(!lp_dos_filemode(SNUM(conn
)))
505 /* We want DOS semantics, ie allow non owner with write permission to change the
506 bits on a file. Just like file_ntimes below.
509 /* Check if we have write access. */
510 if (CAN_WRITE(conn
)) {
512 * We need to open the file with write access whilst
513 * still in our current user context. This ensures we
514 * are not violating security in doing the fchmod.
515 * This file open does *not* break any oplocks we are
516 * holding. We need to review this.... may need to
517 * break batch oplocks open by others. JRA.
520 if (!NT_STATUS_IS_OK(open_file_fchmod(conn
,fname
,st
,&fsp
)))
523 ret
= SMB_VFS_FCHMOD(fsp
, fsp
->fh
->fd
, unixmode
);
525 close_file_fchmod(fsp
);
526 notify_fname(conn
, NOTIFY_ACTION_MODIFIED
,
527 FILE_NOTIFY_CHANGE_ATTRIBUTES
, fname
);
533 /*******************************************************************
534 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
536 *******************************************************************/
538 int file_ntimes(connection_struct
*conn
, const char *fname
, const struct timespec ts
[2])
540 SMB_STRUCT_STAT sbuf
;
546 /* Don't update the time on read-only shares */
547 /* We need this as set_filetime (which can be called on
548 close and other paths) can end up calling this function
549 without the NEED_WRITE protection. Found by :
550 Leo Weppelman <leo@wau.mis.ah.nl>
553 if (!CAN_WRITE(conn
)) {
557 if(SMB_VFS_NTIMES(conn
, fname
, ts
) == 0) {
561 if((errno
!= EPERM
) && (errno
!= EACCES
)) {
565 if(!lp_dos_filetimes(SNUM(conn
))) {
569 /* We have permission (given by the Samba admin) to
570 break POSIX semantics and allow a user to change
571 the time on a file they don't own but can write to
575 /* Check if we have write access. */
576 if (can_write_to_file(conn
, fname
, &sbuf
)) {
577 /* We are allowed to become root and change the filetime. */
579 ret
= SMB_VFS_NTIMES(conn
, fname
, ts
);
586 /*******************************************************************
587 Change a filetime - possibly allowing DOS semantics.
588 *******************************************************************/
590 BOOL
set_filetime(connection_struct
*conn
, const char *fname
,
591 const struct timespec mtime
)
593 struct timespec ts
[2];
595 if (null_timespec(mtime
)) {
599 ts
[1] = mtime
; /* mtime. */
600 ts
[0] = ts
[1]; /* atime. */
602 if (file_ntimes(conn
, fname
, ts
)) {
603 DEBUG(4,("set_filetime(%s) failed: %s\n",fname
,strerror(errno
)));
607 notify_fname(conn
, NOTIFY_ACTION_MODIFIED
,
608 FILE_NOTIFY_CHANGE_LAST_WRITE
, fname
);