r23784: use the GPLv3 boilerplate as recommended by the FSF and the license text
[Samba/bb.git] / source / smbd / dosmode.c
blobb4ab0ca2268c6b7b4b611e202d86fee7a977aee7
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_size > sbuf->st_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
27 return FILE_ATTRIBUTE_SPARSE;
29 #endif
30 return 0;
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)) {
40 return 0;
43 if (!lp_dmapi_support(SNUM(conn)) || !dmapi_have_session()) {
44 return 0;
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.
55 else
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
61 if !inheriting {
62 Then apply create mask,
63 then add force bits.
65 Base permission for directories:
66 dos directory is represented in unix by unix's dir bit and the exec bit
67 if !inheriting {
68 Then apply create mask,
69 then add force bits.
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
78 * inheriting. */
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))) {
86 SMB_STRUCT_STAT sbuf;
88 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
89 inherit_from_dir));
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));
99 /* Clear "result" */
100 result = 0;
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);
108 if (dir_mode) {
109 /* Inherit mode of parent directory. */
110 result |= dir_mode;
111 } else {
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));
120 } else {
121 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
122 result |= S_IXUSR;
124 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
125 result |= S_IXGRP;
127 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
128 result |= S_IXOTH;
130 if (dir_mode) {
131 /* Inherit 666 component of parent directory mode */
132 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
133 } else {
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 ));
142 return(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)
151 int result = 0;
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) {
157 result |= aRONLY;
159 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
160 /* Check actual permissions for read-only. */
161 if (!can_write_to_file(conn, path, sbuf)) {
162 result |= aRONLY;
164 } /* Else never set the readonly bit. */
166 if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
167 result |= aARCH;
169 if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
170 result |= aSYSTEM;
172 if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
173 result |= aHIDDEN;
175 if (S_ISDIR(sbuf->st_mode))
176 result = aDIR | (result & aRONLY);
178 result |= set_sparse_flag(sbuf);
180 #ifdef S_ISLNK
181 #if LINKS_READ_ONLY
182 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
183 result |= aRONLY;
184 #endif
185 #endif
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"));
195 DEBUG(8,("\n"));
196 return result;
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)
205 ssize_t sizeret;
206 fstring attrstr;
207 unsigned int dosattr;
209 if (!lp_store_dos_attributes(SNUM(conn))) {
210 return False;
213 /* Don't reset pattr to zero as we may already have filename-based attributes we
214 need to preserve. */
216 sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
217 if (sizeret == -1) {
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);
224 #endif
225 return 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));
234 return False;
237 if (S_ISDIR(sbuf->st_mode)) {
238 dosattr |= aDIR;
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"));
250 DEBUG(8,("\n"));
252 return True;
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)
261 fstring attrstr;
262 files_struct *fsp = NULL;
263 BOOL ret = False;
265 if (!lp_store_dos_attributes(SNUM(conn))) {
266 return False;
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)) {
272 if (errno == ENOSYS
273 #if defined(ENOTSUP)
274 || errno == ENOTSUP) {
275 #else
277 #endif
278 set_store_dos_attributes(SNUM(conn), False);
280 return 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)))
289 return False;
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)))
298 return ret;
299 become_root();
300 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
301 ret = True;
303 unbecome_root();
304 close_file_fchmod(fsp);
305 return ret;
307 DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
308 return True;
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)
317 uint32 result = 0;
319 DEBUG(8,("dos_mode_msdfs: %s\n", path));
321 if (!VALID_STAT(*sbuf)) {
322 return 0;
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,'/');
329 if (p) {
330 p++;
331 } else {
332 p = path;
335 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
336 result |= aHIDDEN;
340 result |= dos_mode_from_sbuf(conn, path, sbuf);
342 /* Optimization : Only call is_hidden_path if it's not already
343 hidden. */
344 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
345 result |= aHIDDEN;
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]"));
357 DEBUG(8,("\n"));
359 return(result);
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)
368 uint32 result = 0;
370 DEBUG(8,("dos_mode: %s\n", path));
372 if (!VALID_STAT(*sbuf)) {
373 return 0;
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,'/');
380 if (p) {
381 p++;
382 } else {
383 p = path;
386 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
387 result |= aHIDDEN;
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);
394 } else {
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
403 hidden. */
404 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
405 result |= aHIDDEN;
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]"));
417 DEBUG(8,("\n"));
419 return(result);
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)
430 SMB_STRUCT_STAT st1;
431 int mask=0;
432 mode_t tmp;
433 mode_t unixmode;
434 int ret = -1;
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))) {
441 st = &st1;
442 if (SMB_VFS_STAT(conn,fname,st))
443 return(-1);
446 get_acl_group_bits(conn, fname, &st->st_mode);
448 if (S_ISDIR(st->st_mode))
449 dosmode |= aDIR;
450 else
451 dosmode &= ~aDIR;
453 if (dos_mode(conn,fname,st) == dosmode)
454 return(0);
456 /* Store the DOS attributes in an EA by preference. */
457 if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
458 return 0;
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 */
467 #ifdef S_ISVTX
468 mask |= S_ISVTX;
469 #endif
471 /* possibly preserve the x bits */
472 if (!MAP_ARCHIVE(conn))
473 mask |= S_IXUSR;
474 if (!MAP_SYSTEM(conn))
475 mask |= S_IXGRP;
476 if (!MAP_HIDDEN(conn))
477 mask |= S_IXOTH;
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);
484 unixmode |= tmp;
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);
496 return 0;
499 if((errno != EPERM) && (errno != EACCES))
500 return -1;
502 if(!lp_dos_filemode(SNUM(conn)))
503 return -1;
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.
519 files_struct *fsp;
520 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
521 return -1;
522 become_root();
523 ret = SMB_VFS_FCHMOD(fsp, fsp->fh->fd, unixmode);
524 unbecome_root();
525 close_file_fchmod(fsp);
526 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
527 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
530 return( ret );
533 /*******************************************************************
534 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
535 than POSIX.
536 *******************************************************************/
538 int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2])
540 SMB_STRUCT_STAT sbuf;
541 int ret = -1;
543 errno = 0;
544 ZERO_STRUCT(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)) {
554 return 0;
557 if(SMB_VFS_NTIMES(conn, fname, ts) == 0) {
558 return 0;
561 if((errno != EPERM) && (errno != EACCES)) {
562 return -1;
565 if(!lp_dos_filetimes(SNUM(conn))) {
566 return -1;
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
572 (as DOS does).
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. */
578 become_root();
579 ret = SMB_VFS_NTIMES(conn, fname, ts);
580 unbecome_root();
583 return ret;
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)) {
596 return(True);
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)));
604 return False;
607 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
608 FILE_NOTIFY_CHANGE_LAST_WRITE, fname);
610 return True;