Initial import
[samba] / source / smbd / dosmode.c
1 /* 
2    Unix SMB/CIFS implementation.
3    dos mode handling functions
4    Copyright (C) Andrew Tridgell 1992-1998
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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  Change a dos mode to a unix mode.
35     Base permission for files:
36          if creating file and inheriting
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.
47          }
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.
53          }
54 ****************************************************************************/
55
56 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname, BOOL creating_file)
57 {
58         mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
59         mode_t dir_mode = 0; /* Mode of the parent directory if inheriting. */
60
61         if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
62                 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
63         }
64
65         if (fname && creating_file && lp_inherit_perms(SNUM(conn))) {
66                 char *dname;
67                 SMB_STRUCT_STAT sbuf;
68
69                 dname = parent_dirname(fname);
70                 DEBUG(2,("unix_mode(%s) inheriting from %s\n",fname,dname));
71                 if (SMB_VFS_STAT(conn,dname,&sbuf) != 0) {
72                         DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",fname,dname,strerror(errno)));
73                         return(0);      /* *** shouldn't happen! *** */
74                 }
75
76                 /* Save for later - but explicitly remove setuid bit for safety. */
77                 dir_mode = sbuf.st_mode & ~S_ISUID;
78                 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
79                 /* Clear "result" */
80                 result = 0;
81         } 
82
83         if (IS_DOS_DIR(dosmode)) {
84                 /* We never make directories read only for the owner as under DOS a user
85                 can always create a file in a read-only directory. */
86                 result |= (S_IFDIR | S_IWUSR);
87
88                 if (dir_mode) {
89                         /* Inherit mode of parent directory. */
90                         result |= dir_mode;
91                 } else {
92                         /* Provisionally add all 'x' bits */
93                         result |= (S_IXUSR | S_IXGRP | S_IXOTH);                 
94
95                         /* Apply directory mask */
96                         result &= lp_dir_mask(SNUM(conn));
97                         /* Add in force bits */
98                         result |= lp_force_dir_mode(SNUM(conn));
99                 }
100         } else { 
101                 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
102                         result |= S_IXUSR;
103
104                 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
105                         result |= S_IXGRP;
106  
107                 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
108                         result |= S_IXOTH;  
109
110                 if (dir_mode) {
111                         /* Inherit 666 component of parent directory mode */
112                         result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
113                 } else {
114                         /* Apply mode mask */
115                         result &= lp_create_mask(SNUM(conn));
116                         /* Add in force bits */
117                         result |= lp_force_create_mode(SNUM(conn));
118                 }
119         }
120
121         DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
122         return(result);
123 }
124
125 /****************************************************************************
126  Change a unix mode to a dos mode.
127 ****************************************************************************/
128
129 static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
130 {
131         int result = 0;
132         enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
133
134         if (ro_opts == MAP_READONLY_YES) {
135                 /* Original Samba method - map inverse of user "w" bit. */
136                 if ((sbuf->st_mode & S_IWUSR) == 0) {
137                         result |= aRONLY;
138                 }
139         } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
140                 /* Check actual permissions for read-only. */
141                 if (!can_write_to_file(conn, path, sbuf)) {
142                         result |= aRONLY;
143                 }
144         } /* Else never set the readonly bit. */
145
146         if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
147                 result |= aARCH;
148
149         if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
150                 result |= aSYSTEM;
151         
152         if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
153                 result |= aHIDDEN;   
154   
155         if (S_ISDIR(sbuf->st_mode))
156                 result = aDIR | (result & aRONLY);
157
158         result |= set_sparse_flag(sbuf);
159  
160 #ifdef S_ISLNK
161 #if LINKS_READ_ONLY
162         if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
163                 result |= aRONLY;
164 #endif
165 #endif
166
167         DEBUG(8,("dos_mode_from_sbuf returning "));
168
169         if (result & aHIDDEN) DEBUG(8, ("h"));
170         if (result & aRONLY ) DEBUG(8, ("r"));
171         if (result & aSYSTEM) DEBUG(8, ("s"));
172         if (result & aDIR   ) DEBUG(8, ("d"));
173         if (result & aARCH  ) DEBUG(8, ("a"));
174         
175         DEBUG(8,("\n"));
176         return result;
177 }
178
179 /****************************************************************************
180  Get DOS attributes from an EA.
181 ****************************************************************************/
182
183 static BOOL get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
184 {
185         ssize_t sizeret;
186         fstring attrstr;
187         unsigned int dosattr;
188
189         if (!lp_store_dos_attributes(SNUM(conn))) {
190                 return False;
191         }
192
193         /* Don't reset pattr to zero as we may already have filename-based attributes we
194            need to preserve. */
195
196         sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
197         if (sizeret == -1) {
198 #if defined(ENOTSUP) && defined(ENOATTR)
199                 if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES)) {
200                         DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
201                                 path, strerror(errno) ));
202                         set_store_dos_attributes(SNUM(conn), False);
203                 }
204 #endif
205                 return False;
206         }
207         /* Null terminate string. */
208         attrstr[sizeret] = 0;
209         DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
210
211         if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
212                         sscanf(attrstr, "%x", &dosattr) != 1) {
213                 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
214                 return False;
215         }
216
217         if (S_ISDIR(sbuf->st_mode)) {
218                 dosattr |= aDIR;
219         }
220         *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
221
222         DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
223
224         if (dosattr & aHIDDEN) DEBUG(8, ("h"));
225         if (dosattr & aRONLY ) DEBUG(8, ("r"));
226         if (dosattr & aSYSTEM) DEBUG(8, ("s"));
227         if (dosattr & aDIR   ) DEBUG(8, ("d"));
228         if (dosattr & aARCH  ) DEBUG(8, ("a"));
229         
230         DEBUG(8,("\n"));
231
232         return True;
233 }
234
235 /****************************************************************************
236  Set DOS attributes in an EA.
237 ****************************************************************************/
238
239 static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
240 {
241         fstring attrstr;
242         files_struct *fsp = NULL;
243         BOOL ret = False;
244
245         if (!lp_store_dos_attributes(SNUM(conn))) {
246                 return False;
247         }
248
249         snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
250         if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
251                 if((errno != EPERM) && (errno != EACCES)) {
252                         if (errno == ENOSYS
253 #if defined(ENOTSUP)
254                                 || errno == ENOTSUP) {
255 #else
256                                 ) {
257 #endif
258                                 set_store_dos_attributes(SNUM(conn), False);
259                         }
260                         return False;
261                 }
262
263                 /* We want DOS semantics, ie allow non owner with write permission to change the
264                         bits on a file. Just like file_utime below.
265                 */
266
267                 /* Check if we have write access. */
268                 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
269                         return False;
270
271                 /*
272                  * We need to open the file with write access whilst
273                  * still in our current user context. This ensures we
274                  * are not violating security in doing the setxattr.
275                  */
276
277                 fsp = open_file_fchmod(conn,path,sbuf);
278                 if (!fsp)
279                         return ret;
280                 become_root();
281                 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
282                         ret = True;
283                 }
284                 unbecome_root();
285                 close_file_fchmod(fsp);
286                 return ret;
287         }
288         DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
289         return True;
290 }
291
292 /****************************************************************************
293  Change a unix mode to a dos mode.
294 ****************************************************************************/
295
296 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
297 {
298         uint32 result = 0;
299
300         DEBUG(8,("dos_mode: %s\n", path));
301
302         if (!VALID_STAT(*sbuf)) {
303                 return 0;
304         }
305
306         /* First do any modifications that depend on the path name. */
307         /* hide files with a name starting with a . */
308         if (lp_hide_dot_files(SNUM(conn))) {
309                 const char *p = strrchr_m(path,'/');
310                 if (p) {
311                         p++;
312                 } else {
313                         p = path;
314                 }
315                 
316                 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
317                         result |= aHIDDEN;
318                 }
319         }
320         
321         /* Get the DOS attributes from an EA by preference. */
322         if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
323                 result |= set_sparse_flag(sbuf);
324         } else {
325                 result |= dos_mode_from_sbuf(conn, path, sbuf);
326         }
327
328         /* Optimization : Only call is_hidden_path if it's not already
329            hidden. */
330         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
331                 result |= aHIDDEN;
332         }
333
334         DEBUG(8,("dos_mode returning "));
335
336         if (result & aHIDDEN) DEBUG(8, ("h"));
337         if (result & aRONLY ) DEBUG(8, ("r"));
338         if (result & aSYSTEM) DEBUG(8, ("s"));
339         if (result & aDIR   ) DEBUG(8, ("d"));
340         if (result & aARCH  ) DEBUG(8, ("a"));
341         if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
342         
343         DEBUG(8,("\n"));
344
345         return(result);
346 }
347
348 /*******************************************************************
349  chmod a file - but preserve some bits.
350 ********************************************************************/
351
352 int file_set_dosmode(connection_struct *conn, const char *fname, uint32 dosmode, SMB_STRUCT_STAT *st, BOOL creating_file)
353 {
354         SMB_STRUCT_STAT st1;
355         int mask=0;
356         mode_t tmp;
357         mode_t unixmode;
358         int ret = -1;
359
360         /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
361         dosmode &= SAMBA_ATTRIBUTES_MASK;
362
363         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
364         if (!st || (st && !VALID_STAT(*st))) {
365                 st = &st1;
366                 if (SMB_VFS_STAT(conn,fname,st))
367                         return(-1);
368         }
369
370         get_acl_group_bits(conn, fname, &st->st_mode);
371
372         if (S_ISDIR(st->st_mode))
373                 dosmode |= aDIR;
374         else
375                 dosmode &= ~aDIR;
376
377         if (dos_mode(conn,fname,st) == dosmode)
378                 return(0);
379
380         /* Store the DOS attributes in an EA by preference. */
381         if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
382                 return 0;
383         }
384
385         unixmode = unix_mode(conn,dosmode,fname, creating_file);
386
387         /* preserve the s bits */
388         mask |= (S_ISUID | S_ISGID);
389
390         /* preserve the t bit */
391 #ifdef S_ISVTX
392         mask |= S_ISVTX;
393 #endif
394
395         /* possibly preserve the x bits */
396         if (!MAP_ARCHIVE(conn))
397                 mask |= S_IXUSR;
398         if (!MAP_SYSTEM(conn))
399                 mask |= S_IXGRP;
400         if (!MAP_HIDDEN(conn))
401                 mask |= S_IXOTH;
402
403         unixmode |= (st->st_mode & mask);
404
405         /* if we previously had any r bits set then leave them alone */
406         if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
407                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
408                 unixmode |= tmp;
409         }
410
411         /* if we previously had any w bits set then leave them alone 
412                 whilst adding in the new w bits, if the new mode is not rdonly */
413         if (!IS_DOS_READONLY(dosmode)) {
414                 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
415         }
416
417         if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0)
418                 return 0;
419
420         if((errno != EPERM) && (errno != EACCES))
421                 return -1;
422
423         if(!lp_dos_filemode(SNUM(conn)))
424                 return -1;
425
426         /* We want DOS semantics, ie allow non owner with write permission to change the
427                 bits on a file. Just like file_utime below.
428         */
429
430         /* Check if we have write access. */
431         if (CAN_WRITE(conn)) {
432                 /*
433                  * We need to open the file with write access whilst
434                  * still in our current user context. This ensures we
435                  * are not violating security in doing the fchmod.
436                  * This file open does *not* break any oplocks we are
437                  * holding. We need to review this.... may need to
438                  * break batch oplocks open by others. JRA.
439                  */
440                 files_struct *fsp = open_file_fchmod(conn,fname,st);
441                 if (!fsp)
442                         return -1;
443                 become_root();
444                 ret = SMB_VFS_FCHMOD(fsp, fsp->fh->fd, unixmode);
445                 unbecome_root();
446                 close_file_fchmod(fsp);
447         }
448
449         return( ret );
450 }
451
452 /*******************************************************************
453  Wrapper around dos_utime that possibly allows DOS semantics rather
454  than POSIX.
455 *******************************************************************/
456
457 int file_utime(connection_struct *conn, const char *fname, struct utimbuf *times)
458 {
459         SMB_STRUCT_STAT sbuf;
460         int ret = -1;
461
462         errno = 0;
463         ZERO_STRUCT(sbuf);
464
465         /* Don't update the time on read-only shares */
466         /* We need this as set_filetime (which can be called on
467            close and other paths) can end up calling this function
468            without the NEED_WRITE protection. Found by : 
469            Leo Weppelman <leo@wau.mis.ah.nl>
470         */
471
472         if (!CAN_WRITE(conn)) {
473                 return 0;
474         }
475
476         if(SMB_VFS_UTIME(conn,fname, times) == 0)
477                 return 0;
478
479         if((errno != EPERM) && (errno != EACCES))
480                 return -1;
481
482         if(!lp_dos_filetimes(SNUM(conn)))
483                 return -1;
484
485         /* We have permission (given by the Samba admin) to
486            break POSIX semantics and allow a user to change
487            the time on a file they don't own but can write to
488            (as DOS does).
489          */
490
491         /* Check if we have write access. */
492         if (can_write_to_file(conn, fname, &sbuf)) {
493                 /* We are allowed to become root and change the filetime. */
494                 become_root();
495                 ret = SMB_VFS_UTIME(conn,fname, times);
496                 unbecome_root();
497         }
498
499         return ret;
500 }
501   
502 /*******************************************************************
503  Change a filetime - possibly allowing DOS semantics.
504 *******************************************************************/
505
506 BOOL set_filetime(connection_struct *conn, const char *fname, time_t mtime)
507 {
508         struct utimbuf times;
509
510         if (null_mtime(mtime))
511                 return(True);
512
513         times.modtime = times.actime = mtime;
514
515         if (file_utime(conn, fname, &times)) {
516                 DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
517                 return False;
518         }
519   
520         return(True);
521