Ensure files starting with multiple dots are hidden
[Samba.git] / source / smbd / dosmode.c
blob8a5a7b0d0e2cafca943cb0d3992f6b40ef8c5e49
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 Change a dos mode to a unix mode.
35 Base permission for files:
36 if creating file and inheriting (i.e. parent_dir != NULL)
37 apply read/write bits from parent directory.
38 else
39 everybody gets read bit set
40 dos readonly is represented in unix by removing everyone's write bit
41 dos archive is represented in unix by the user's execute bit
42 dos system is represented in unix by the group's execute bit
43 dos hidden is represented in unix by the other's execute bit
44 if !inheriting {
45 Then apply create mask,
46 then add force bits.
48 Base permission for directories:
49 dos directory is represented in unix by unix's dir bit and the exec bit
50 if !inheriting {
51 Then apply create mask,
52 then add force bits.
54 ****************************************************************************/
56 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
57 const char *inherit_from_dir)
59 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
60 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
61 * inheriting. */
63 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
64 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
67 if (fname && (inherit_from_dir != NULL)
68 && lp_inherit_perms(SNUM(conn))) {
69 SMB_STRUCT_STAT sbuf;
71 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
72 inherit_from_dir));
73 if (SMB_VFS_STAT(conn, inherit_from_dir, &sbuf) != 0) {
74 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname,
75 inherit_from_dir, strerror(errno)));
76 return(0); /* *** shouldn't happen! *** */
79 /* Save for later - but explicitly remove setuid bit for safety. */
80 dir_mode = sbuf.st_mode & ~S_ISUID;
81 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
82 /* Clear "result" */
83 result = 0;
86 if (IS_DOS_DIR(dosmode)) {
87 /* We never make directories read only for the owner as under DOS a user
88 can always create a file in a read-only directory. */
89 result |= (S_IFDIR | S_IWUSR);
91 if (dir_mode) {
92 /* Inherit mode of parent directory. */
93 result |= dir_mode;
94 } else {
95 /* Provisionally add all 'x' bits */
96 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
98 /* Apply directory mask */
99 result &= lp_dir_mask(SNUM(conn));
100 /* Add in force bits */
101 result |= lp_force_dir_mode(SNUM(conn));
103 } else {
104 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
105 result |= S_IXUSR;
107 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
108 result |= S_IXGRP;
110 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
111 result |= S_IXOTH;
113 if (dir_mode) {
114 /* Inherit 666 component of parent directory mode */
115 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
116 } else {
117 /* Apply mode mask */
118 result &= lp_create_mask(SNUM(conn));
119 /* Add in force bits */
120 result |= lp_force_create_mode(SNUM(conn));
124 DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
125 return(result);
128 /****************************************************************************
129 Change a unix mode to a dos mode.
130 ****************************************************************************/
132 static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
134 int result = 0;
135 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
137 if (ro_opts == MAP_READONLY_YES) {
138 /* Original Samba method - map inverse of user "w" bit. */
139 if ((sbuf->st_mode & S_IWUSR) == 0) {
140 result |= aRONLY;
142 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
143 /* Check actual permissions for read-only. */
144 if (!can_write_to_file(conn, path, sbuf)) {
145 result |= aRONLY;
147 } /* Else never set the readonly bit. */
149 if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
150 result |= aARCH;
152 if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
153 result |= aSYSTEM;
155 if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
156 result |= aHIDDEN;
158 if (S_ISDIR(sbuf->st_mode))
159 result = aDIR | (result & aRONLY);
161 result |= set_sparse_flag(sbuf);
163 #ifdef S_ISLNK
164 #if LINKS_READ_ONLY
165 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
166 result |= aRONLY;
167 #endif
168 #endif
170 DEBUG(8,("dos_mode_from_sbuf returning "));
172 if (result & aHIDDEN) DEBUG(8, ("h"));
173 if (result & aRONLY ) DEBUG(8, ("r"));
174 if (result & aSYSTEM) DEBUG(8, ("s"));
175 if (result & aDIR ) DEBUG(8, ("d"));
176 if (result & aARCH ) DEBUG(8, ("a"));
178 DEBUG(8,("\n"));
179 return result;
182 /****************************************************************************
183 Get DOS attributes from an EA.
184 ****************************************************************************/
186 static bool get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
188 ssize_t sizeret;
189 fstring attrstr;
190 unsigned int dosattr;
192 if (!lp_store_dos_attributes(SNUM(conn))) {
193 return False;
196 /* Don't reset pattr to zero as we may already have filename-based attributes we
197 need to preserve. */
199 sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
200 if (sizeret == -1) {
201 if (errno == ENOSYS
202 #if defined(ENOTSUP)
203 || errno == ENOTSUP) {
204 #else
206 #endif
207 DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
208 path, strerror(errno) ));
209 set_store_dos_attributes(SNUM(conn), False);
211 return False;
213 /* Null terminate string. */
214 attrstr[sizeret] = 0;
215 DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
217 if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
218 sscanf(attrstr, "%x", &dosattr) != 1) {
219 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
220 return False;
223 if (S_ISDIR(sbuf->st_mode)) {
224 dosattr |= aDIR;
226 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
228 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
230 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
231 if (dosattr & aRONLY ) DEBUG(8, ("r"));
232 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
233 if (dosattr & aDIR ) DEBUG(8, ("d"));
234 if (dosattr & aARCH ) DEBUG(8, ("a"));
236 DEBUG(8,("\n"));
238 return True;
241 /****************************************************************************
242 Set DOS attributes in an EA.
243 ****************************************************************************/
245 static bool set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
247 fstring attrstr;
248 files_struct *fsp = NULL;
249 bool ret = False;
251 if (!lp_store_dos_attributes(SNUM(conn))) {
252 return False;
255 snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
256 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
257 if((errno != EPERM) && (errno != EACCES)) {
258 if (errno == ENOSYS
259 #if defined(ENOTSUP)
260 || errno == ENOTSUP) {
261 #else
263 #endif
264 DEBUG(1,("set_ea_dos_attributes: Cannot set attribute EA on file %s: Error = %s\n",
265 path, strerror(errno) ));
266 set_store_dos_attributes(SNUM(conn), False);
268 return False;
271 /* We want DOS semantics, ie allow non owner with write permission to change the
272 bits on a file. Just like file_ntimes below.
275 /* Check if we have write access. */
276 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
277 return False;
280 * We need to open the file with write access whilst
281 * still in our current user context. This ensures we
282 * are not violating security in doing the setxattr.
285 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,path,sbuf,&fsp)))
286 return ret;
287 become_root();
288 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
289 ret = True;
291 unbecome_root();
292 close_file_fchmod(fsp);
293 return ret;
295 DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
296 return True;
299 /****************************************************************************
300 Change a unix mode to a dos mode for an ms dfs link.
301 ****************************************************************************/
303 uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
305 uint32 result = 0;
307 DEBUG(8,("dos_mode_msdfs: %s\n", path));
309 if (!VALID_STAT(*sbuf)) {
310 return 0;
313 /* First do any modifications that depend on the path name. */
314 /* hide files with a name starting with a . */
315 if (lp_hide_dot_files(SNUM(conn))) {
316 const char *p = strrchr_m(path,'/');
317 if (p) {
318 p++;
319 } else {
320 p = path;
323 /* Only . and .. are not hidden. */
324 if (p[0] == '.' && !((p[1] == '\0') ||
325 (p[1] == '.' && p[2] == '\0'))) {
326 result |= aHIDDEN;
330 result |= dos_mode_from_sbuf(conn, path, sbuf);
332 /* Optimization : Only call is_hidden_path if it's not already
333 hidden. */
334 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
335 result |= aHIDDEN;
338 DEBUG(8,("dos_mode_msdfs returning "));
340 if (result & aHIDDEN) DEBUG(8, ("h"));
341 if (result & aRONLY ) DEBUG(8, ("r"));
342 if (result & aSYSTEM) DEBUG(8, ("s"));
343 if (result & aDIR ) DEBUG(8, ("d"));
344 if (result & aARCH ) DEBUG(8, ("a"));
345 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
347 DEBUG(8,("\n"));
349 return(result);
352 /****************************************************************************
353 Change a unix mode to a dos mode.
354 ****************************************************************************/
356 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
358 uint32 result = 0;
359 bool offline;
361 DEBUG(8,("dos_mode: %s\n", path));
363 if (!VALID_STAT(*sbuf)) {
364 return 0;
367 /* First do any modifications that depend on the path name. */
368 /* hide files with a name starting with a . */
369 if (lp_hide_dot_files(SNUM(conn))) {
370 const char *p = strrchr_m(path,'/');
371 if (p) {
372 p++;
373 } else {
374 p = path;
377 /* Only . and .. are not hidden. */
378 if (p[0] == '.' && !((p[1] == '\0') ||
379 (p[1] == '.' && p[2] == '\0'))) {
380 result |= aHIDDEN;
384 /* Get the DOS attributes from an EA by preference. */
385 if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
386 result |= set_sparse_flag(sbuf);
387 } else {
388 result |= dos_mode_from_sbuf(conn, path, sbuf);
392 offline = SMB_VFS_IS_OFFLINE(conn, path, sbuf);
393 if (S_ISREG(sbuf->st_mode) && offline) {
394 result |= FILE_ATTRIBUTE_OFFLINE;
397 /* Optimization : Only call is_hidden_path if it's not already
398 hidden. */
399 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
400 result |= aHIDDEN;
403 DEBUG(8,("dos_mode returning "));
405 if (result & aHIDDEN) DEBUG(8, ("h"));
406 if (result & aRONLY ) DEBUG(8, ("r"));
407 if (result & aSYSTEM) DEBUG(8, ("s"));
408 if (result & aDIR ) DEBUG(8, ("d"));
409 if (result & aARCH ) DEBUG(8, ("a"));
410 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
412 DEBUG(8,("\n"));
414 return(result);
417 /*******************************************************************
418 chmod a file - but preserve some bits.
419 ********************************************************************/
421 int file_set_dosmode(connection_struct *conn, const char *fname,
422 uint32 dosmode, SMB_STRUCT_STAT *st,
423 const char *parent_dir,
424 bool newfile)
426 SMB_STRUCT_STAT st1;
427 int mask=0;
428 mode_t tmp;
429 mode_t unixmode;
430 int ret = -1, lret = -1;
431 uint32_t old_mode;
433 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
434 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
436 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
438 if (st == NULL) {
439 SET_STAT_INVALID(st1);
440 st = &st1;
443 if (!VALID_STAT(*st)) {
444 if (SMB_VFS_STAT(conn,fname,st))
445 return(-1);
448 unixmode = st->st_mode;
450 get_acl_group_bits(conn, fname, &st->st_mode);
452 if (S_ISDIR(st->st_mode))
453 dosmode |= aDIR;
454 else
455 dosmode &= ~aDIR;
457 old_mode = dos_mode(conn,fname,st);
459 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
460 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
461 lret = SMB_VFS_SET_OFFLINE(conn, fname);
462 if (lret == -1) {
463 DEBUG(0, ("set_dos_mode: client has asked to set "
464 "FILE_ATTRIBUTE_OFFLINE to %s/%s but there was "
465 "an error while setting it or it is not supported.\n",
466 parent_dir, fname));
471 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
472 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
474 if (old_mode == dosmode) {
475 st->st_mode = unixmode;
476 return(0);
479 /* Store the DOS attributes in an EA by preference. */
480 if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
481 if (!newfile) {
482 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
483 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
485 st->st_mode = unixmode;
486 return 0;
489 unixmode = unix_mode(conn,dosmode,fname, parent_dir);
491 /* preserve the s bits */
492 mask |= (S_ISUID | S_ISGID);
494 /* preserve the t bit */
495 #ifdef S_ISVTX
496 mask |= S_ISVTX;
497 #endif
499 /* possibly preserve the x bits */
500 if (!MAP_ARCHIVE(conn))
501 mask |= S_IXUSR;
502 if (!MAP_SYSTEM(conn))
503 mask |= S_IXGRP;
504 if (!MAP_HIDDEN(conn))
505 mask |= S_IXOTH;
507 unixmode |= (st->st_mode & mask);
509 /* if we previously had any r bits set then leave them alone */
510 if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
511 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
512 unixmode |= tmp;
515 /* if we previously had any w bits set then leave them alone
516 whilst adding in the new w bits, if the new mode is not rdonly */
517 if (!IS_DOS_READONLY(dosmode)) {
518 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
521 ret = SMB_VFS_CHMOD(conn, fname, unixmode);
522 if (ret == 0) {
523 if(!newfile || (lret != -1)) {
524 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
525 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
527 st->st_mode = unixmode;
528 return 0;
531 if((errno != EPERM) && (errno != EACCES))
532 return -1;
534 if(!lp_dos_filemode(SNUM(conn)))
535 return -1;
537 /* We want DOS semantics, ie allow non owner with write permission to change the
538 bits on a file. Just like file_ntimes below.
541 /* Check if we have write access. */
542 if (CAN_WRITE(conn)) {
544 * We need to open the file with write access whilst
545 * still in our current user context. This ensures we
546 * are not violating security in doing the fchmod.
547 * This file open does *not* break any oplocks we are
548 * holding. We need to review this.... may need to
549 * break batch oplocks open by others. JRA.
551 files_struct *fsp;
552 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
553 return -1;
554 become_root();
555 ret = SMB_VFS_FCHMOD(fsp, unixmode);
556 unbecome_root();
557 close_file_fchmod(fsp);
558 if (!newfile) {
559 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
560 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
562 if (ret == 0) {
563 st->st_mode = unixmode;
567 return( ret );
570 /*******************************************************************
571 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
572 than POSIX.
573 *******************************************************************/
575 int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2])
577 SMB_STRUCT_STAT sbuf;
578 int ret = -1;
580 errno = 0;
581 ZERO_STRUCT(sbuf);
583 DEBUG(6, ("file_ntime: actime: %s",
584 time_to_asc(convert_timespec_to_time_t(ts[0]))));
585 DEBUG(6, ("file_ntime: modtime: %s",
586 time_to_asc(convert_timespec_to_time_t(ts[1]))));
588 /* Don't update the time on read-only shares */
589 /* We need this as set_filetime (which can be called on
590 close and other paths) can end up calling this function
591 without the NEED_WRITE protection. Found by :
592 Leo Weppelman <leo@wau.mis.ah.nl>
595 if (!CAN_WRITE(conn)) {
596 return 0;
599 if(SMB_VFS_NTIMES(conn, fname, ts) == 0) {
600 return 0;
603 if((errno != EPERM) && (errno != EACCES)) {
604 return -1;
607 if(!lp_dos_filetimes(SNUM(conn))) {
608 return -1;
611 /* We have permission (given by the Samba admin) to
612 break POSIX semantics and allow a user to change
613 the time on a file they don't own but can write to
614 (as DOS does).
617 /* Check if we have write access. */
618 if (can_write_to_file(conn, fname, &sbuf)) {
619 /* We are allowed to become root and change the filetime. */
620 become_root();
621 ret = SMB_VFS_NTIMES(conn, fname, ts);
622 unbecome_root();
625 return ret;
628 /******************************************************************
629 Force a "sticky" write time on a pathname. This will always be
630 returned on all future write time queries and set on close.
631 ******************************************************************/
633 bool set_sticky_write_time_path(connection_struct *conn, const char *fname,
634 struct file_id fileid, const struct timespec mtime)
636 if (null_timespec(mtime)) {
637 return true;
640 if (!set_sticky_write_time(fileid, mtime)) {
641 return false;
644 return true;
647 /******************************************************************
648 Force a "sticky" write time on an fsp. This will always be
649 returned on all future write time queries and set on close.
650 ******************************************************************/
652 bool set_sticky_write_time_fsp(struct files_struct *fsp, const struct timespec mtime)
654 fsp->write_time_forced = true;
655 TALLOC_FREE(fsp->update_write_time_event);
657 return set_sticky_write_time_path(fsp->conn, fsp->fsp_name,
658 fsp->file_id, mtime);
661 /******************************************************************
662 Update a write time immediately, without the 2 second delay.
663 ******************************************************************/
665 bool update_write_time(struct files_struct *fsp)
667 if (!set_write_time(fsp->file_id, timespec_current())) {
668 return false;
671 notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
672 FILE_NOTIFY_CHANGE_LAST_WRITE, fsp->fsp_name);
674 return true;