b4ab0ca2268c6b7b4b611e202d86fee7a977aee7
[samba.git] / source3 / smbd / dosmode.c
1 /* 
2    Unix SMB/CIFS implementation.
3    dos mode handling functions
4    Copyright (C) Andrew Tridgell 1992-1998
5    Copyright (C) James Peach 2006
6    
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.
11    
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.
16    
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/>.
19 */
20
21 #include "includes.h"
22
23 static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
24 {
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;
28         }
29 #endif
30         return 0;
31 }
32
33 /****************************************************************************
34  Work out whether this file is offline
35 ****************************************************************************/
36
37 static uint32 set_offline_flag(connection_struct *conn, const char *const path)
38 {
39         if (ISDOT(path) || ISDOTDOT(path)) {
40                 return 0;
41         }
42
43         if (!lp_dmapi_support(SNUM(conn)) || !dmapi_have_session()) {
44                 return 0;
45         }
46
47         return dmapi_file_flags(path);
48 }
49
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.
64          }
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.
70          }
71 ****************************************************************************/
72
73 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
74                  const char *inherit_from_dir)
75 {
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. */
79
80         if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
81                 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
82         }
83
84         if (fname && (inherit_from_dir != NULL)
85             && lp_inherit_perms(SNUM(conn))) {
86                 SMB_STRUCT_STAT sbuf;
87
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! *** */
94                 }
95
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;
101         } 
102
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);
107
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);                 
114
115                         /* Apply directory mask */
116                         result &= lp_dir_mask(SNUM(conn));
117                         /* Add in force bits */
118                         result |= lp_force_dir_mode(SNUM(conn));
119                 }
120         } else { 
121                 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
122                         result |= S_IXUSR;
123
124                 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
125                         result |= S_IXGRP;
126  
127                 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
128                         result |= S_IXOTH;  
129
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));
138                 }
139         }
140
141         DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
142         return(result);
143 }
144
145 /****************************************************************************
146  Change a unix mode to a dos mode.
147 ****************************************************************************/
148
149 static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
150 {
151         int result = 0;
152         enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
153
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;
158                 }
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;
163                 }
164         } /* Else never set the readonly bit. */
165
166         if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
167                 result |= aARCH;
168
169         if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
170                 result |= aSYSTEM;
171         
172         if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
173                 result |= aHIDDEN;   
174   
175         if (S_ISDIR(sbuf->st_mode))
176                 result = aDIR | (result & aRONLY);
177
178         result |= set_sparse_flag(sbuf);
179  
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
186
187         DEBUG(8,("dos_mode_from_sbuf returning "));
188
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"));
194         
195         DEBUG(8,("\n"));
196         return result;
197 }
198
199 /****************************************************************************
200  Get DOS attributes from an EA.
201 ****************************************************************************/
202
203 static BOOL get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
204 {
205         ssize_t sizeret;
206         fstring attrstr;
207         unsigned int dosattr;
208
209         if (!lp_store_dos_attributes(SNUM(conn))) {
210                 return False;
211         }
212
213         /* Don't reset pattr to zero as we may already have filename-based attributes we
214            need to preserve. */
215
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);
223                 }
224 #endif
225                 return False;
226         }
227         /* Null terminate string. */
228         attrstr[sizeret] = 0;
229         DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
230
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;
235         }
236
237         if (S_ISDIR(sbuf->st_mode)) {
238                 dosattr |= aDIR;
239         }
240         *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
241
242         DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
243
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"));
249         
250         DEBUG(8,("\n"));
251
252         return True;
253 }
254
255 /****************************************************************************
256  Set DOS attributes in an EA.
257 ****************************************************************************/
258
259 static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
260 {
261         fstring attrstr;
262         files_struct *fsp = NULL;
263         BOOL ret = False;
264
265         if (!lp_store_dos_attributes(SNUM(conn))) {
266                 return False;
267         }
268
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
276                                 ) {
277 #endif
278                                 set_store_dos_attributes(SNUM(conn), False);
279                         }
280                         return False;
281                 }
282
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.
285                 */
286
287                 /* Check if we have write access. */
288                 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
289                         return False;
290
291                 /*
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.
295                  */
296
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;
302                 }
303                 unbecome_root();
304                 close_file_fchmod(fsp);
305                 return ret;
306         }
307         DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
308         return True;
309 }
310
311 /****************************************************************************
312  Change a unix mode to a dos mode for an ms dfs link.
313 ****************************************************************************/
314
315 uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
316 {
317         uint32 result = 0;
318
319         DEBUG(8,("dos_mode_msdfs: %s\n", path));
320
321         if (!VALID_STAT(*sbuf)) {
322                 return 0;
323         }
324
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;
333                 }
334                 
335                 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
336                         result |= aHIDDEN;
337                 }
338         }
339         
340         result |= dos_mode_from_sbuf(conn, path, sbuf);
341
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;
346         }
347
348         DEBUG(8,("dos_mode_msdfs returning "));
349
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]"));
356         
357         DEBUG(8,("\n"));
358
359         return(result);
360 }
361
362 /****************************************************************************
363  Change a unix mode to a dos mode.
364 ****************************************************************************/
365
366 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
367 {
368         uint32 result = 0;
369
370         DEBUG(8,("dos_mode: %s\n", path));
371
372         if (!VALID_STAT(*sbuf)) {
373                 return 0;
374         }
375
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;
384                 }
385                 
386                 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
387                         result |= aHIDDEN;
388                 }
389         }
390         
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);
396         }
397
398         if (S_ISREG(sbuf->st_mode)) {
399                 result |= set_offline_flag(conn, path);
400         }
401
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;
406         }
407
408         DEBUG(8,("dos_mode returning "));
409
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]"));
416         
417         DEBUG(8,("\n"));
418
419         return(result);
420 }
421
422 /*******************************************************************
423  chmod a file - but preserve some bits.
424 ********************************************************************/
425
426 int file_set_dosmode(connection_struct *conn, const char *fname,
427                      uint32 dosmode, SMB_STRUCT_STAT *st,
428                      const char *parent_dir)
429 {
430         SMB_STRUCT_STAT st1;
431         int mask=0;
432         mode_t tmp;
433         mode_t unixmode;
434         int ret = -1;
435
436         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
437         dosmode &= SAMBA_ATTRIBUTES_MASK;
438
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);
444         }
445
446         get_acl_group_bits(conn, fname, &st->st_mode);
447
448         if (S_ISDIR(st->st_mode))
449                 dosmode |= aDIR;
450         else
451                 dosmode &= ~aDIR;
452
453         if (dos_mode(conn,fname,st) == dosmode)
454                 return(0);
455
456         /* Store the DOS attributes in an EA by preference. */
457         if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
458                 return 0;
459         }
460
461         unixmode = unix_mode(conn,dosmode,fname, parent_dir);
462
463         /* preserve the s bits */
464         mask |= (S_ISUID | S_ISGID);
465
466         /* preserve the t bit */
467 #ifdef S_ISVTX
468         mask |= S_ISVTX;
469 #endif
470
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;
478
479         unixmode |= (st->st_mode & mask);
480
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;
485         }
486
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));
491         }
492
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;
497         }
498
499         if((errno != EPERM) && (errno != EACCES))
500                 return -1;
501
502         if(!lp_dos_filemode(SNUM(conn)))
503                 return -1;
504
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.
507         */
508
509         /* Check if we have write access. */
510         if (CAN_WRITE(conn)) {
511                 /*
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.
518                  */
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);
528         }
529
530         return( ret );
531 }
532
533 /*******************************************************************
534  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
535  than POSIX.
536 *******************************************************************/
537
538 int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2])
539 {
540         SMB_STRUCT_STAT sbuf;
541         int ret = -1;
542
543         errno = 0;
544         ZERO_STRUCT(sbuf);
545
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>
551         */
552
553         if (!CAN_WRITE(conn)) {
554                 return 0;
555         }
556
557         if(SMB_VFS_NTIMES(conn, fname, ts) == 0) {
558                 return 0;
559         }
560
561         if((errno != EPERM) && (errno != EACCES)) {
562                 return -1;
563         }
564
565         if(!lp_dos_filetimes(SNUM(conn))) {
566                 return -1;
567         }
568
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).
573          */
574
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();
581         }
582
583         return ret;
584 }
585   
586 /*******************************************************************
587  Change a filetime - possibly allowing DOS semantics.
588 *******************************************************************/
589
590 BOOL set_filetime(connection_struct *conn, const char *fname,
591                 const struct timespec mtime)
592 {
593         struct timespec ts[2];
594
595         if (null_timespec(mtime)) {
596                 return(True);
597         }
598
599         ts[1] = mtime; /* mtime. */
600         ts[0] = ts[1]; /* atime. */
601
602         if (file_ntimes(conn, fname, ts)) {
603                 DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
604                 return False;
605         }
606
607         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
608                      FILE_NOTIFY_CHANGE_LAST_WRITE, fname);
609   
610         return True;
611 }