Remove redundant parameter fd from SMB_VFS_FCHMOD().
[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                      bool newfile)
430 {
431         SMB_STRUCT_STAT st1;
432         int mask=0;
433         mode_t tmp;
434         mode_t unixmode;
435         int ret = -1;
436
437         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
438         dosmode &= SAMBA_ATTRIBUTES_MASK;
439
440         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
441
442         if (st == NULL) {
443                 SET_STAT_INVALID(st1);
444                 st = &st1;
445         }
446
447         if (!VALID_STAT(*st)) {
448                 if (SMB_VFS_STAT(conn,fname,st))
449                         return(-1);
450         }
451
452         unixmode = st->st_mode;
453
454         get_acl_group_bits(conn, fname, &st->st_mode);
455
456         if (S_ISDIR(st->st_mode))
457                 dosmode |= aDIR;
458         else
459                 dosmode &= ~aDIR;
460
461         if (dos_mode(conn,fname,st) == dosmode) {
462                 st->st_mode = unixmode;
463                 return(0);
464         }
465
466         /* Store the DOS attributes in an EA by preference. */
467         if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
468                 if (!newfile) {
469                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
470                                 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
471                 }
472                 st->st_mode = unixmode;
473                 return 0;
474         }
475
476         unixmode = unix_mode(conn,dosmode,fname, parent_dir);
477
478         /* preserve the s bits */
479         mask |= (S_ISUID | S_ISGID);
480
481         /* preserve the t bit */
482 #ifdef S_ISVTX
483         mask |= S_ISVTX;
484 #endif
485
486         /* possibly preserve the x bits */
487         if (!MAP_ARCHIVE(conn))
488                 mask |= S_IXUSR;
489         if (!MAP_SYSTEM(conn))
490                 mask |= S_IXGRP;
491         if (!MAP_HIDDEN(conn))
492                 mask |= S_IXOTH;
493
494         unixmode |= (st->st_mode & mask);
495
496         /* if we previously had any r bits set then leave them alone */
497         if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
498                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
499                 unixmode |= tmp;
500         }
501
502         /* if we previously had any w bits set then leave them alone 
503                 whilst adding in the new w bits, if the new mode is not rdonly */
504         if (!IS_DOS_READONLY(dosmode)) {
505                 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
506         }
507
508         if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0) {
509                 if (!newfile) {
510                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
511                                 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
512                 }
513                 st->st_mode = unixmode;
514                 return 0;
515         }
516
517         if((errno != EPERM) && (errno != EACCES))
518                 return -1;
519
520         if(!lp_dos_filemode(SNUM(conn)))
521                 return -1;
522
523         /* We want DOS semantics, ie allow non owner with write permission to change the
524                 bits on a file. Just like file_ntimes below.
525         */
526
527         /* Check if we have write access. */
528         if (CAN_WRITE(conn)) {
529                 /*
530                  * We need to open the file with write access whilst
531                  * still in our current user context. This ensures we
532                  * are not violating security in doing the fchmod.
533                  * This file open does *not* break any oplocks we are
534                  * holding. We need to review this.... may need to
535                  * break batch oplocks open by others. JRA.
536                  */
537                 files_struct *fsp;
538                 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
539                         return -1;
540                 become_root();
541                 ret = SMB_VFS_FCHMOD(fsp, unixmode);
542                 unbecome_root();
543                 close_file_fchmod(fsp);
544                 if (!newfile) {
545                         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
546                                 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
547                 }
548                 if (ret == 0) {
549                         st->st_mode = unixmode;
550                 }
551         }
552
553         return( ret );
554 }
555
556 /*******************************************************************
557  Wrapper around the VFS ntimes that possibly allows DOS semantics rather
558  than POSIX.
559 *******************************************************************/
560
561 int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2])
562 {
563         SMB_STRUCT_STAT sbuf;
564         int ret = -1;
565
566         errno = 0;
567         ZERO_STRUCT(sbuf);
568
569         /* Don't update the time on read-only shares */
570         /* We need this as set_filetime (which can be called on
571            close and other paths) can end up calling this function
572            without the NEED_WRITE protection. Found by : 
573            Leo Weppelman <leo@wau.mis.ah.nl>
574         */
575
576         if (!CAN_WRITE(conn)) {
577                 return 0;
578         }
579
580         if(SMB_VFS_NTIMES(conn, fname, ts) == 0) {
581                 return 0;
582         }
583
584         if((errno != EPERM) && (errno != EACCES)) {
585                 return -1;
586         }
587
588         if(!lp_dos_filetimes(SNUM(conn))) {
589                 return -1;
590         }
591
592         /* We have permission (given by the Samba admin) to
593            break POSIX semantics and allow a user to change
594            the time on a file they don't own but can write to
595            (as DOS does).
596          */
597
598         /* Check if we have write access. */
599         if (can_write_to_file(conn, fname, &sbuf)) {
600                 /* We are allowed to become root and change the filetime. */
601                 become_root();
602                 ret = SMB_VFS_NTIMES(conn, fname, ts);
603                 unbecome_root();
604         }
605
606         return ret;
607 }
608
609 /*******************************************************************
610  Change a filetime - possibly allowing DOS semantics.
611 *******************************************************************/
612
613 bool set_filetime(connection_struct *conn, const char *fname,
614                 const struct timespec mtime)
615 {
616         struct timespec ts[2];
617
618         if (null_timespec(mtime)) {
619                 return(True);
620         }
621
622         ts[1] = mtime; /* mtime. */
623         ts[0] = ts[1]; /* atime. */
624
625         if (file_ntimes(conn, fname, ts)) {
626                 DEBUG(4,("set_filetime(%s) failed: %s\n",
627                                         fname,strerror(errno)));
628                 return False;
629         }
630
631         notify_fname(conn, NOTIFY_ACTION_MODIFIED,
632                 FILE_NOTIFY_CHANGE_LAST_WRITE, fname);
633
634         return true;
635 }