Fix bug 6875 - trans2 FIND_FIRST2 response --> FIND_FIRST2 Data -> Fille Attributes...
[Samba.git] / source / smbd / dosmode.c
blob07e1103dc9badc5113d05b5500a3203abc096850
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 int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
27 #if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
28 if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
29 return FILE_ATTRIBUTE_SPARSE;
31 #endif
32 return 0;
35 /****************************************************************************
36 Change a dos mode to a unix mode.
37 Base permission for files:
38 if creating file and inheriting (i.e. parent_dir != NULL)
39 apply read/write bits from parent directory.
40 else
41 everybody gets read bit set
42 dos readonly is represented in unix by removing everyone's write bit
43 dos archive is represented in unix by the user's execute bit
44 dos system is represented in unix by the group's execute bit
45 dos hidden is represented in unix by the other's execute bit
46 if !inheriting {
47 Then apply create mask,
48 then add force bits.
50 Base permission for directories:
51 dos directory is represented in unix by unix's dir bit and the exec bit
52 if !inheriting {
53 Then apply create mask,
54 then add force bits.
56 ****************************************************************************/
58 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
59 const char *inherit_from_dir)
61 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
62 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
63 * inheriting. */
65 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
66 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
69 if (fname && (inherit_from_dir != NULL)
70 && lp_inherit_perms(SNUM(conn))) {
71 SMB_STRUCT_STAT sbuf;
73 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
74 inherit_from_dir));
75 if (SMB_VFS_STAT(conn, inherit_from_dir, &sbuf) != 0) {
76 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname,
77 inherit_from_dir, strerror(errno)));
78 return(0); /* *** shouldn't happen! *** */
81 /* Save for later - but explicitly remove setuid bit for safety. */
82 dir_mode = sbuf.st_mode & ~S_ISUID;
83 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
84 /* Clear "result" */
85 result = 0;
88 if (IS_DOS_DIR(dosmode)) {
89 /* We never make directories read only for the owner as under DOS a user
90 can always create a file in a read-only directory. */
91 result |= (S_IFDIR | S_IWUSR);
93 if (dir_mode) {
94 /* Inherit mode of parent directory. */
95 result |= dir_mode;
96 } else {
97 /* Provisionally add all 'x' bits */
98 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
100 /* Apply directory mask */
101 result &= lp_dir_mask(SNUM(conn));
102 /* Add in force bits */
103 result |= lp_force_dir_mode(SNUM(conn));
105 } else {
106 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
107 result |= S_IXUSR;
109 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
110 result |= S_IXGRP;
112 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
113 result |= S_IXOTH;
115 if (dir_mode) {
116 /* Inherit 666 component of parent directory mode */
117 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
118 } else {
119 /* Apply mode mask */
120 result &= lp_create_mask(SNUM(conn));
121 /* Add in force bits */
122 result |= lp_force_create_mode(SNUM(conn));
126 DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
127 return(result);
130 /****************************************************************************
131 Change a unix mode to a dos mode.
132 ****************************************************************************/
134 static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
136 int result = 0;
137 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
139 if (ro_opts == MAP_READONLY_YES) {
140 /* Original Samba method - map inverse of user "w" bit. */
141 if ((sbuf->st_mode & S_IWUSR) == 0) {
142 result |= aRONLY;
144 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
145 /* Check actual permissions for read-only. */
146 if (!can_write_to_file(conn, path, sbuf)) {
147 result |= aRONLY;
149 } /* Else never set the readonly bit. */
151 if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
152 result |= aARCH;
154 if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
155 result |= aSYSTEM;
157 if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
158 result |= aHIDDEN;
160 if (S_ISDIR(sbuf->st_mode))
161 result = aDIR | (result & aRONLY);
163 result |= set_sparse_flag(sbuf);
165 #ifdef S_ISLNK
166 #if LINKS_READ_ONLY
167 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
168 result |= aRONLY;
169 #endif
170 #endif
172 DEBUG(8,("dos_mode_from_sbuf returning "));
174 if (result & aHIDDEN) DEBUG(8, ("h"));
175 if (result & aRONLY ) DEBUG(8, ("r"));
176 if (result & aSYSTEM) DEBUG(8, ("s"));
177 if (result & aDIR ) DEBUG(8, ("d"));
178 if (result & aARCH ) DEBUG(8, ("a"));
180 DEBUG(8,("\n"));
181 return result;
184 /****************************************************************************
185 Get DOS attributes from an EA.
186 ****************************************************************************/
188 static bool get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
190 ssize_t sizeret;
191 fstring attrstr;
192 unsigned int dosattr;
194 if (!lp_store_dos_attributes(SNUM(conn))) {
195 return False;
198 /* Don't reset pattr to zero as we may already have filename-based attributes we
199 need to preserve. */
201 sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
202 if (sizeret == -1) {
203 if (errno == ENOSYS
204 #if defined(ENOTSUP)
205 || errno == ENOTSUP) {
206 #else
208 #endif
209 DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
210 path, strerror(errno) ));
211 set_store_dos_attributes(SNUM(conn), False);
213 return False;
215 /* Null terminate string. */
216 attrstr[sizeret] = 0;
217 DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
219 if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
220 sscanf(attrstr, "%x", &dosattr) != 1) {
221 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
222 return False;
225 if (S_ISDIR(sbuf->st_mode)) {
226 dosattr |= aDIR;
228 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
230 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
232 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
233 if (dosattr & aRONLY ) DEBUG(8, ("r"));
234 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
235 if (dosattr & aDIR ) DEBUG(8, ("d"));
236 if (dosattr & aARCH ) DEBUG(8, ("a"));
238 DEBUG(8,("\n"));
240 return True;
243 /****************************************************************************
244 Set DOS attributes in an EA.
245 ****************************************************************************/
247 static bool set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
249 fstring attrstr;
250 files_struct *fsp = NULL;
251 bool ret = False;
253 if (!lp_store_dos_attributes(SNUM(conn))) {
254 return False;
257 snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
258 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
259 if((errno != EPERM) && (errno != EACCES)) {
260 if (errno == ENOSYS
261 #if defined(ENOTSUP)
262 || errno == ENOTSUP) {
263 #else
265 #endif
266 DEBUG(1,("set_ea_dos_attributes: Cannot set attribute EA on file %s: Error = %s\n",
267 path, strerror(errno) ));
268 set_store_dos_attributes(SNUM(conn), False);
270 return False;
273 /* We want DOS semantics, ie allow non owner with write permission to change the
274 bits on a file. Just like file_ntimes below.
277 /* Check if we have write access. */
278 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
279 return False;
282 * We need to open the file with write access whilst
283 * still in our current user context. This ensures we
284 * are not violating security in doing the setxattr.
287 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,path,sbuf,&fsp)))
288 return ret;
289 become_root();
290 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
291 ret = True;
293 unbecome_root();
294 close_file_fchmod(fsp);
295 return ret;
297 DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
298 return True;
301 /****************************************************************************
302 Change a unix mode to a dos mode for an ms dfs link.
303 ****************************************************************************/
305 uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
307 uint32 result = 0;
309 DEBUG(8,("dos_mode_msdfs: %s\n", path));
311 if (!VALID_STAT(*sbuf)) {
312 return 0;
315 /* First do any modifications that depend on the path name. */
316 /* hide files with a name starting with a . */
317 if (lp_hide_dot_files(SNUM(conn))) {
318 const char *p = strrchr_m(path,'/');
319 if (p) {
320 p++;
321 } else {
322 p = path;
325 /* Only . and .. are not hidden. */
326 if (p[0] == '.' && !((p[1] == '\0') ||
327 (p[1] == '.' && p[2] == '\0'))) {
328 result |= aHIDDEN;
332 result |= dos_mode_from_sbuf(conn, path, sbuf);
334 /* Optimization : Only call is_hidden_path if it's not already
335 hidden. */
336 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
337 result |= aHIDDEN;
340 if (Protocol <= PROTOCOL_LANMAN2) {
341 DEBUG(10,("dos_mode_msdfs : filtering result 0x%x\n",
342 (unsigned int)result ));
343 result &= 0xff;
346 DEBUG(8,("dos_mode_msdfs returning "));
348 if (result & aHIDDEN) DEBUG(8, ("h"));
349 if (result & aRONLY ) DEBUG(8, ("r"));
350 if (result & aSYSTEM) DEBUG(8, ("s"));
351 if (result & aDIR ) DEBUG(8, ("d"));
352 if (result & aARCH ) DEBUG(8, ("a"));
353 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
355 DEBUG(8,("\n"));
357 return(result);
360 /****************************************************************************
361 Change a unix mode to a dos mode.
362 ****************************************************************************/
364 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
366 uint32 result = 0;
367 bool offline;
369 DEBUG(8,("dos_mode: %s\n", path));
371 if (!VALID_STAT(*sbuf)) {
372 return 0;
375 /* First do any modifications that depend on the path name. */
376 /* hide files with a name starting with a . */
377 if (lp_hide_dot_files(SNUM(conn))) {
378 const char *p = strrchr_m(path,'/');
379 if (p) {
380 p++;
381 } else {
382 p = path;
385 /* Only . and .. are not hidden. */
386 if (p[0] == '.' && !((p[1] == '\0') ||
387 (p[1] == '.' && p[2] == '\0'))) {
388 result |= aHIDDEN;
392 /* Get the DOS attributes from an EA by preference. */
393 if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
394 result |= set_sparse_flag(sbuf);
395 } else {
396 result |= dos_mode_from_sbuf(conn, path, sbuf);
400 offline = SMB_VFS_IS_OFFLINE(conn, path, sbuf);
401 if (S_ISREG(sbuf->st_mode) && offline) {
402 result |= FILE_ATTRIBUTE_OFFLINE;
405 /* Optimization : Only call is_hidden_path if it's not already
406 hidden. */
407 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
408 result |= aHIDDEN;
411 if (Protocol <= PROTOCOL_LANMAN2) {
412 DEBUG(10,("dos_mode : filtering result 0x%x\n",
413 (unsigned int)result ));
414 result &= 0xff;
417 DEBUG(8,("dos_mode returning "));
419 if (result & aHIDDEN) DEBUG(8, ("h"));
420 if (result & aRONLY ) DEBUG(8, ("r"));
421 if (result & aSYSTEM) DEBUG(8, ("s"));
422 if (result & aDIR ) DEBUG(8, ("d"));
423 if (result & aARCH ) DEBUG(8, ("a"));
424 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
426 DEBUG(8,("\n"));
428 return(result);
431 /*******************************************************************
432 chmod a file - but preserve some bits.
433 ********************************************************************/
435 int file_set_dosmode(connection_struct *conn, const char *fname,
436 uint32 dosmode, SMB_STRUCT_STAT *st,
437 const char *parent_dir,
438 bool newfile)
440 SMB_STRUCT_STAT st1;
441 int mask=0;
442 mode_t tmp;
443 mode_t unixmode;
444 int ret = -1, lret = -1;
445 uint32_t old_mode;
447 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
448 dosmode &= (SAMBA_ATTRIBUTES_MASK | FILE_ATTRIBUTE_OFFLINE);
450 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
452 if (st == NULL) {
453 SET_STAT_INVALID(st1);
454 st = &st1;
457 if (!VALID_STAT(*st)) {
458 if (SMB_VFS_STAT(conn,fname,st))
459 return(-1);
462 unixmode = st->st_mode;
464 get_acl_group_bits(conn, fname, &st->st_mode);
466 if (S_ISDIR(st->st_mode))
467 dosmode |= aDIR;
468 else
469 dosmode &= ~aDIR;
471 old_mode = dos_mode(conn,fname,st);
473 if (dosmode & FILE_ATTRIBUTE_OFFLINE) {
474 if (!(old_mode & FILE_ATTRIBUTE_OFFLINE)) {
475 lret = SMB_VFS_SET_OFFLINE(conn, fname);
476 if (lret == -1) {
477 DEBUG(0, ("set_dos_mode: client has asked to set "
478 "FILE_ATTRIBUTE_OFFLINE to %s/%s but there was "
479 "an error while setting it or it is not supported.\n",
480 parent_dir, fname));
485 dosmode &= ~FILE_ATTRIBUTE_OFFLINE;
486 old_mode &= ~FILE_ATTRIBUTE_OFFLINE;
488 if (old_mode == dosmode) {
489 st->st_mode = unixmode;
490 return(0);
493 /* Store the DOS attributes in an EA by preference. */
494 if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
495 if (!newfile) {
496 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
497 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
499 st->st_mode = unixmode;
500 return 0;
503 unixmode = unix_mode(conn,dosmode,fname, parent_dir);
505 /* preserve the s bits */
506 mask |= (S_ISUID | S_ISGID);
508 /* preserve the t bit */
509 #ifdef S_ISVTX
510 mask |= S_ISVTX;
511 #endif
513 /* possibly preserve the x bits */
514 if (!MAP_ARCHIVE(conn))
515 mask |= S_IXUSR;
516 if (!MAP_SYSTEM(conn))
517 mask |= S_IXGRP;
518 if (!MAP_HIDDEN(conn))
519 mask |= S_IXOTH;
521 unixmode |= (st->st_mode & mask);
523 /* if we previously had any r bits set then leave them alone */
524 if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
525 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
526 unixmode |= tmp;
529 /* if we previously had any w bits set then leave them alone
530 whilst adding in the new w bits, if the new mode is not rdonly */
531 if (!IS_DOS_READONLY(dosmode)) {
532 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
535 ret = SMB_VFS_CHMOD(conn, fname, unixmode);
536 if (ret == 0) {
537 if(!newfile || (lret != -1)) {
538 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
539 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
541 st->st_mode = unixmode;
542 return 0;
545 if((errno != EPERM) && (errno != EACCES))
546 return -1;
548 if(!lp_dos_filemode(SNUM(conn)))
549 return -1;
551 /* We want DOS semantics, ie allow non owner with write permission to change the
552 bits on a file. Just like file_ntimes below.
555 /* Check if we have write access. */
556 if (CAN_WRITE(conn)) {
558 * We need to open the file with write access whilst
559 * still in our current user context. This ensures we
560 * are not violating security in doing the fchmod.
561 * This file open does *not* break any oplocks we are
562 * holding. We need to review this.... may need to
563 * break batch oplocks open by others. JRA.
565 files_struct *fsp;
566 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
567 return -1;
568 become_root();
569 ret = SMB_VFS_FCHMOD(fsp, unixmode);
570 unbecome_root();
571 close_file_fchmod(fsp);
572 if (!newfile) {
573 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
574 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
576 if (ret == 0) {
577 st->st_mode = unixmode;
581 return( ret );
584 /*******************************************************************
585 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
586 than POSIX.
587 *******************************************************************/
589 int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2])
591 SMB_STRUCT_STAT sbuf;
592 int ret = -1;
594 errno = 0;
595 ZERO_STRUCT(sbuf);
597 DEBUG(6, ("file_ntime: actime: %s",
598 time_to_asc(convert_timespec_to_time_t(ts[0]))));
599 DEBUG(6, ("file_ntime: modtime: %s",
600 time_to_asc(convert_timespec_to_time_t(ts[1]))));
602 /* Don't update the time on read-only shares */
603 /* We need this as set_filetime (which can be called on
604 close and other paths) can end up calling this function
605 without the NEED_WRITE protection. Found by :
606 Leo Weppelman <leo@wau.mis.ah.nl>
609 if (!CAN_WRITE(conn)) {
610 return 0;
613 if(SMB_VFS_NTIMES(conn, fname, ts) == 0) {
614 return 0;
617 if((errno != EPERM) && (errno != EACCES)) {
618 return -1;
621 if(!lp_dos_filetimes(SNUM(conn))) {
622 return -1;
625 /* We have permission (given by the Samba admin) to
626 break POSIX semantics and allow a user to change
627 the time on a file they don't own but can write to
628 (as DOS does).
631 /* Check if we have write access. */
632 if (can_write_to_file(conn, fname, &sbuf)) {
633 /* We are allowed to become root and change the filetime. */
634 become_root();
635 ret = SMB_VFS_NTIMES(conn, fname, ts);
636 unbecome_root();
639 return ret;
642 /******************************************************************
643 Force a "sticky" write time on a pathname. This will always be
644 returned on all future write time queries and set on close.
645 ******************************************************************/
647 bool set_sticky_write_time_path(connection_struct *conn, const char *fname,
648 struct file_id fileid, const struct timespec mtime)
650 if (null_timespec(mtime)) {
651 return true;
654 if (!set_sticky_write_time(fileid, mtime)) {
655 return false;
658 return true;
661 /******************************************************************
662 Force a "sticky" write time on an fsp. This will always be
663 returned on all future write time queries and set on close.
664 ******************************************************************/
666 bool set_sticky_write_time_fsp(struct files_struct *fsp, const struct timespec mtime)
668 fsp->write_time_forced = true;
669 TALLOC_FREE(fsp->update_write_time_event);
671 return set_sticky_write_time_path(fsp->conn, fsp->fsp_name,
672 fsp->file_id, mtime);
675 /******************************************************************
676 Update a write time immediately, without the 2 second delay.
677 ******************************************************************/
679 bool update_write_time(struct files_struct *fsp)
681 if (!set_write_time(fsp->file_id, timespec_current())) {
682 return false;
685 notify_fname(fsp->conn, NOTIFY_ACTION_MODIFIED,
686 FILE_NOTIFY_CHANGE_LAST_WRITE, fsp->fsp_name);
688 return true;