Fix bug #7155 - valgrind Conditional jump or move depends on uninitialised value...
[Samba.git] / source / smbd / dosmode.c
blobc7c9f3eae9ca6d7837c7c2e9117bc2b62ff2347b
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 extern enum protocol_types Protocol;
25 static uint32_t filter_mode_by_protocol(uint32_t mode)
27 if (Protocol <= PROTOCOL_LANMAN2) {
28 DEBUG(10,("filter_mode_by_protocol: "
29 "filtering result 0x%x to 0x%x\n",
30 (unsigned int)mode,
31 (unsigned int)(mode & 0x3f) ));
32 mode &= 0x3f;
34 return mode;
37 static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
39 #if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
40 if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
41 return FILE_ATTRIBUTE_SPARSE;
43 #endif
44 return 0;
47 /****************************************************************************
48 Change a dos mode to a unix mode.
49 Base permission for files:
50 if creating file and inheriting (i.e. parent_dir != NULL)
51 apply read/write bits from parent directory.
52 else
53 everybody gets read bit set
54 dos readonly is represented in unix by removing everyone's write bit
55 dos archive is represented in unix by the user's execute bit
56 dos system is represented in unix by the group's execute bit
57 dos hidden is represented in unix by the other's execute bit
58 if !inheriting {
59 Then apply create mask,
60 then add force bits.
62 Base permission for directories:
63 dos directory is represented in unix by unix's dir bit and the exec bit
64 if !inheriting {
65 Then apply create mask,
66 then add force bits.
68 ****************************************************************************/
70 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
71 const char *inherit_from_dir)
73 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
74 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
75 * inheriting. */
77 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
78 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
81 if (fname && (inherit_from_dir != NULL)
82 && lp_inherit_perms(SNUM(conn))) {
83 SMB_STRUCT_STAT sbuf;
85 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
86 inherit_from_dir));
87 if (SMB_VFS_STAT(conn, inherit_from_dir, &sbuf) != 0) {
88 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname,
89 inherit_from_dir, strerror(errno)));
90 return(0); /* *** shouldn't happen! *** */
93 /* Save for later - but explicitly remove setuid bit for safety. */
94 dir_mode = sbuf.st_mode & ~S_ISUID;
95 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
96 /* Clear "result" */
97 result = 0;
100 if (IS_DOS_DIR(dosmode)) {
101 /* We never make directories read only for the owner as under DOS a user
102 can always create a file in a read-only directory. */
103 result |= (S_IFDIR | S_IWUSR);
105 if (dir_mode) {
106 /* Inherit mode of parent directory. */
107 result |= dir_mode;
108 } else {
109 /* Provisionally add all 'x' bits */
110 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
112 /* Apply directory mask */
113 result &= lp_dir_mask(SNUM(conn));
114 /* Add in force bits */
115 result |= lp_force_dir_mode(SNUM(conn));
117 } else {
118 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
119 result |= S_IXUSR;
121 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
122 result |= S_IXGRP;
124 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
125 result |= S_IXOTH;
127 if (dir_mode) {
128 /* Inherit 666 component of parent directory mode */
129 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
130 } else {
131 /* Apply mode mask */
132 result &= lp_create_mask(SNUM(conn));
133 /* Add in force bits */
134 result |= lp_force_create_mode(SNUM(conn));
138 DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
139 return(result);
142 /****************************************************************************
143 Change a unix mode to a dos mode.
144 ****************************************************************************/
146 static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
148 int result = 0;
149 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
151 if (ro_opts == MAP_READONLY_YES) {
152 /* Original Samba method - map inverse of user "w" bit. */
153 if ((sbuf->st_mode & S_IWUSR) == 0) {
154 result |= aRONLY;
156 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
157 /* Check actual permissions for read-only. */
158 if (!can_write_to_file(conn, path, sbuf)) {
159 result |= aRONLY;
161 } /* Else never set the readonly bit. */
163 if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
164 result |= aARCH;
166 if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
167 result |= aSYSTEM;
169 if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
170 result |= aHIDDEN;
172 if (S_ISDIR(sbuf->st_mode))
173 result = aDIR | (result & aRONLY);
175 result |= set_sparse_flag(sbuf);
177 #ifdef S_ISLNK
178 #if LINKS_READ_ONLY
179 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
180 result |= aRONLY;
181 #endif
182 #endif
184 DEBUG(8,("dos_mode_from_sbuf returning "));
186 if (result & aHIDDEN) DEBUG(8, ("h"));
187 if (result & aRONLY ) DEBUG(8, ("r"));
188 if (result & aSYSTEM) DEBUG(8, ("s"));
189 if (result & aDIR ) DEBUG(8, ("d"));
190 if (result & aARCH ) DEBUG(8, ("a"));
192 DEBUG(8,("\n"));
193 return result;
196 /****************************************************************************
197 Get DOS attributes from an EA.
198 ****************************************************************************/
200 static bool get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
202 ssize_t sizeret;
203 fstring attrstr;
204 unsigned int dosattr;
206 if (!lp_store_dos_attributes(SNUM(conn))) {
207 return False;
210 /* Don't reset pattr to zero as we may already have filename-based attributes we
211 need to preserve. */
213 sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
214 if (sizeret == -1) {
215 if (errno == ENOSYS
216 #if defined(ENOTSUP)
217 || errno == ENOTSUP) {
218 #else
220 #endif
221 DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
222 path, strerror(errno) ));
223 set_store_dos_attributes(SNUM(conn), False);
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 DEBUG(1,("set_ea_dos_attributes: Cannot set attribute EA on file %s: Error = %s\n",
279 path, strerror(errno) ));
280 set_store_dos_attributes(SNUM(conn), False);
282 return False;
285 /* We want DOS semantics, ie allow non owner with write permission to change the
286 bits on a file. Just like file_ntimes below.
289 /* Check if we have write access. */
290 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
291 return False;
294 * We need to open the file with write access whilst
295 * still in our current user context. This ensures we
296 * are not violating security in doing the setxattr.
299 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,path,sbuf,&fsp)))
300 return ret;
301 become_root();
302 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
303 ret = True;
305 unbecome_root();
306 close_file_fchmod(fsp);
307 return ret;
309 DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
310 return True;
313 /****************************************************************************
314 Change a unix mode to a dos mode for an ms dfs link.
315 ****************************************************************************/
317 uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
319 uint32 result = 0;
321 DEBUG(8,("dos_mode_msdfs: %s\n", path));
323 if (!VALID_STAT(*sbuf)) {
324 return 0;
327 /* First do any modifications that depend on the path name. */
328 /* hide files with a name starting with a . */
329 if (lp_hide_dot_files(SNUM(conn))) {
330 const char *p = strrchr_m(path,'/');
331 if (p) {
332 p++;
333 } else {
334 p = path;
337 /* Only . and .. are not hidden. */
338 if (p[0] == '.' && !((p[1] == '\0') ||
339 (p[1] == '.' && p[2] == '\0'))) {
340 result |= aHIDDEN;
344 result |= dos_mode_from_sbuf(conn, path, sbuf);
346 /* Optimization : Only call is_hidden_path if it's not already
347 hidden. */
348 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
349 result |= aHIDDEN;
352 if (result == 0) {
353 result = FILE_ATTRIBUTE_NORMAL;
356 result = filter_mode_by_protocol(result);
358 DEBUG(8,("dos_mode_msdfs returning "));
360 if (result & aHIDDEN) DEBUG(8, ("h"));
361 if (result & aRONLY ) DEBUG(8, ("r"));
362 if (result & aSYSTEM) DEBUG(8, ("s"));
363 if (result & aDIR ) DEBUG(8, ("d"));
364 if (result & aARCH ) DEBUG(8, ("a"));
365 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
367 DEBUG(8,("\n"));
369 return(result);
372 /****************************************************************************
373 Change a unix mode to a dos mode.
374 ****************************************************************************/
376 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
378 uint32 result = 0;
379 bool offline;
381 DEBUG(8,("dos_mode: %s\n", path));
383 if (!VALID_STAT(*sbuf)) {
384 return 0;
387 /* First do any modifications that depend on the path name. */
388 /* hide files with a name starting with a . */
389 if (lp_hide_dot_files(SNUM(conn))) {
390 const char *p = strrchr_m(path,'/');
391 if (p) {
392 p++;
393 } else {
394 p = path;
397 /* Only . and .. are not hidden. */
398 if (p[0] == '.' && !((p[1] == '\0') ||
399 (p[1] == '.' && p[2] == '\0'))) {
400 result |= aHIDDEN;
404 /* Get the DOS attributes from an EA by preference. */
405 if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
406 result |= set_sparse_flag(sbuf);
407 } else {
408 result |= dos_mode_from_sbuf(conn, path, sbuf);
412 offline = SMB_VFS_IS_OFFLINE(conn, path, sbuf);
413 if (S_ISREG(sbuf->st_mode) && offline) {
414 result |= FILE_ATTRIBUTE_OFFLINE;
417 /* Optimization : Only call is_hidden_path if it's not already
418 hidden. */
419 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
420 result |= aHIDDEN;
423 if (result == 0) {
424 result = FILE_ATTRIBUTE_NORMAL;
427 result = filter_mode_by_protocol(result);
429 DEBUG(8,("dos_mode returning "));
431 if (result & aHIDDEN) DEBUG(8, ("h"));
432 if (result & aRONLY ) DEBUG(8, ("r"));
433 if (result & aSYSTEM) DEBUG(8, ("s"));
434 if (result & aDIR ) DEBUG(8, ("d"));
435 if (result & aARCH ) DEBUG(8, ("a"));
436 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
438 DEBUG(8,("\n"));
440 return(result);
443 /*******************************************************************
444 chmod a file - but preserve some bits.
445 ********************************************************************/
447 int file_set_dosmode(connection_struct *conn, const char *fname,
448 uint32 dosmode, SMB_STRUCT_STAT *st,
449 const char *parent_dir,
450 bool newfile)
452 SMB_STRUCT_STAT st1;
453 int mask=0;
454 mode_t tmp;
455 mode_t unixmode;
456 int ret = -1, lret = -1;
457 uint32_t old_mode;
459 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
460 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
462 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
464 if (st == NULL) {
465 SET_STAT_INVALID(st1);
466 st = &st1;
469 if (!VALID_STAT(*st)) {
470 if (SMB_VFS_STAT(conn,fname,st))
471 return(-1);
474 unixmode = st->st_mode;
476 get_acl_group_bits(conn, fname, &st->st_mode);
478 if (S_ISDIR(st->st_mode))
479 dosmode |= aDIR;
480 else
481 dosmode &= ~aDIR;
483 old_mode = dos_mode(conn,fname,st);
485 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
486 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
487 lret = SMB_VFS_SET_OFFLINE(conn, fname);
488 if (lret == -1) {
489 DEBUG(0, ("set_dos_mode: client has asked to set "
490 "FILE_ATTRIBUTE_OFFLINE to %s/%s but there was "
491 "an error while setting it or it is not supported.\n",
492 parent_dir, fname));
497 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
498 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
500 if (old_mode == dosmode) {
501 st->st_mode = unixmode;
502 return(0);
505 /* Store the DOS attributes in an EA by preference. */
506 if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
507 if (!newfile) {
508 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
509 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
511 st->st_mode = unixmode;
512 return 0;
515 unixmode = unix_mode(conn,dosmode,fname, parent_dir);
517 /* preserve the s bits */
518 mask |= (S_ISUID | S_ISGID);
520 /* preserve the t bit */
521 #ifdef S_ISVTX
522 mask |= S_ISVTX;
523 #endif
525 /* possibly preserve the x bits */
526 if (!MAP_ARCHIVE(conn))
527 mask |= S_IXUSR;
528 if (!MAP_SYSTEM(conn))
529 mask |= S_IXGRP;
530 if (!MAP_HIDDEN(conn))
531 mask |= S_IXOTH;
533 unixmode |= (st->st_mode & mask);
535 /* if we previously had any r bits set then leave them alone */
536 if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
537 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
538 unixmode |= tmp;
541 /* if we previously had any w bits set then leave them alone
542 whilst adding in the new w bits, if the new mode is not rdonly */
543 if (!IS_DOS_READONLY(dosmode)) {
544 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
547 ret = SMB_VFS_CHMOD(conn, fname, unixmode);
548 if (ret == 0) {
549 if(!newfile || (lret != -1)) {
550 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
551 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
553 st->st_mode = unixmode;
554 return 0;
557 if((errno != EPERM) && (errno != EACCES))
558 return -1;
560 if(!lp_dos_filemode(SNUM(conn)))
561 return -1;
563 /* We want DOS semantics, ie allow non owner with write permission to change the
564 bits on a file. Just like file_ntimes below.
567 /* Check if we have write access. */
568 if (CAN_WRITE(conn)) {
570 * We need to open the file with write access whilst
571 * still in our current user context. This ensures we
572 * are not violating security in doing the fchmod.
573 * This file open does *not* break any oplocks we are
574 * holding. We need to review this.... may need to
575 * break batch oplocks open by others. JRA.
577 files_struct *fsp;
578 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
579 return -1;
580 become_root();
581 ret = SMB_VFS_FCHMOD(fsp, unixmode);
582 unbecome_root();
583 close_file_fchmod(fsp);
584 if (!newfile) {
585 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
586 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
588 if (ret == 0) {
589 st->st_mode = unixmode;
593 return( ret );
596 /*******************************************************************
597 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
598 than POSIX.
599 *******************************************************************/
601 int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2])
603 SMB_STRUCT_STAT sbuf;
604 int ret = -1;
606 errno = 0;
607 ZERO_STRUCT(sbuf);
609 DEBUG(6, ("file_ntime: actime: %s",
610 time_to_asc(convert_timespec_to_time_t(ts[0]))));
611 DEBUG(6, ("file_ntime: modtime: %s",
612 time_to_asc(convert_timespec_to_time_t(ts[1]))));
614 /* Don't update the time on read-only shares */
615 /* We need this as set_filetime (which can be called on
616 close and other paths) can end up calling this function
617 without the NEED_WRITE protection. Found by :
618 Leo Weppelman <leo@wau.mis.ah.nl>
621 if (!CAN_WRITE(conn)) {
622 return 0;
625 if(SMB_VFS_NTIMES(conn, fname, ts) == 0) {
626 return 0;
629 if((errno != EPERM) && (errno != EACCES)) {
630 return -1;
633 if(!lp_dos_filetimes(SNUM(conn))) {
634 return -1;
637 /* We have permission (given by the Samba admin) to
638 break POSIX semantics and allow a user to change
639 the time on a file they don't own but can write to
640 (as DOS does).
643 /* Check if we have write access. */
644 if (can_write_to_file(conn, fname, &sbuf)) {
645 /* We are allowed to become root and change the filetime. */
646 become_root();
647 ret = SMB_VFS_NTIMES(conn, fname, ts);
648 unbecome_root();
651 return ret;
654 /******************************************************************
655 Force a "sticky" write time on a pathname. This will always be
656 returned on all future write time queries and set on close.
657 ******************************************************************/
659 bool set_sticky_write_time_path(connection_struct *conn, const char *fname,
660 struct file_id fileid, const struct timespec mtime)
662 if (null_timespec(mtime)) {
663 return true;
666 if (!set_sticky_write_time(fileid, mtime)) {
667 return false;
670 return true;
673 /******************************************************************
674 Force a "sticky" write time on an fsp. This will always be
675 returned on all future write time queries and set on close.
676 ******************************************************************/
678 bool set_sticky_write_time_fsp(struct files_struct *fsp, const struct timespec mtime)
680 fsp->write_time_forced = true;
681 TALLOC_FREE(fsp->update_write_time_event);
683 return set_sticky_write_time_path(fsp->conn, fsp->fsp_name,
684 fsp->file_id, mtime);
687 /******************************************************************
688 Update a write time immediately, without the 2 second delay.
689 ******************************************************************/
691 bool update_write_time(struct files_struct *fsp)
693 if (!set_write_time(fsp->file_id, timespec_current())) {
694 return false;
697 notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
698 FILE_NOTIFY_CHANGE_LAST_WRITE, fsp->fsp_name);
700 return true;