Added per-share parameter "store dos attributes". When set, will store
[ira/wip.git] / source3 / 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 /****************************************************************************
24  Change a dos mode to a unix mode.
25     Base permission for files:
26          if inheriting
27            apply read/write bits from parent directory.
28          else   
29            everybody gets read bit set
30          dos readonly is represented in unix by removing everyone's write bit
31          dos archive is represented in unix by the user's execute bit
32          dos system is represented in unix by the group's execute bit
33          dos hidden is represented in unix by the other's execute bit
34          if !inheriting {
35            Then apply create mask,
36            then add force bits.
37          }
38     Base permission for directories:
39          dos directory is represented in unix by unix's dir bit and the exec bit
40          if !inheriting {
41            Then apply create mask,
42            then add force bits.
43          }
44 ****************************************************************************/
45
46 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname)
47 {
48         mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
49         mode_t dir_mode = 0; /* Mode of the parent directory if inheriting. */
50
51         if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
52                 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
53         }
54
55         if (fname && lp_inherit_perms(SNUM(conn))) {
56                 char *dname;
57                 SMB_STRUCT_STAT sbuf;
58
59                 dname = parent_dirname(fname);
60                 DEBUG(2,("unix_mode(%s) inheriting from %s\n",fname,dname));
61                 if (SMB_VFS_STAT(conn,dname,&sbuf) != 0) {
62                         DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",fname,dname,strerror(errno)));
63                         return(0);      /* *** shouldn't happen! *** */
64                 }
65
66                 /* Save for later - but explicitly remove setuid bit for safety. */
67                 dir_mode = sbuf.st_mode & ~S_ISUID;
68                 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
69                 /* Clear "result" */
70                 result = 0;
71         } 
72
73         if (IS_DOS_DIR(dosmode)) {
74                 /* We never make directories read only for the owner as under DOS a user
75                 can always create a file in a read-only directory. */
76                 result |= (S_IFDIR | S_IWUSR);
77
78                 if (dir_mode) {
79                         /* Inherit mode of parent directory. */
80                         result |= dir_mode;
81                 } else {
82                         /* Provisionally add all 'x' bits */
83                         result |= (S_IXUSR | S_IXGRP | S_IXOTH);                 
84
85                         /* Apply directory mask */
86                         result &= lp_dir_mask(SNUM(conn));
87                         /* Add in force bits */
88                         result |= lp_force_dir_mode(SNUM(conn));
89                 }
90         } else { 
91                 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
92                         result |= S_IXUSR;
93
94                 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
95                         result |= S_IXGRP;
96  
97                 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
98                         result |= S_IXOTH;  
99
100                 if (dir_mode) {
101                         /* Inherit 666 component of parent directory mode */
102                         result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
103                 } else {
104                         /* Apply mode mask */
105                         result &= lp_create_mask(SNUM(conn));
106                         /* Add in force bits */
107                         result |= lp_force_create_mode(SNUM(conn));
108                 }
109         }
110
111         DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
112         return(result);
113 }
114
115 /****************************************************************************
116  Change a unix mode to a dos mode.
117 ****************************************************************************/
118
119 uint32 dos_mode_from_sbuf(connection_struct *conn, SMB_STRUCT_STAT *sbuf)
120 {
121         int result = 0;
122
123         if ((sbuf->st_mode & S_IWUSR) == 0)
124                 result |= aRONLY;
125         
126         if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
127                 result |= aARCH;
128
129         if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
130                 result |= aSYSTEM;
131         
132         if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
133                 result |= aHIDDEN;   
134   
135         if (S_ISDIR(sbuf->st_mode))
136                 result = aDIR | (result & aRONLY);
137
138 #if defined (HAVE_STAT_ST_BLOCKS) && defined (HAVE_STAT_ST_BLKSIZE)
139         if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)sbuf->st_blksize) {
140                 result |= FILE_ATTRIBUTE_SPARSE;
141         }
142 #endif
143  
144 #ifdef S_ISLNK
145 #if LINKS_READ_ONLY
146         if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
147                 result |= aRONLY;
148 #endif
149 #endif
150
151         DEBUG(8,("dos_mode_from_sbuf returning "));
152
153         if (result & aHIDDEN) DEBUG(8, ("h"));
154         if (result & aRONLY ) DEBUG(8, ("r"));
155         if (result & aSYSTEM) DEBUG(8, ("s"));
156         if (result & aDIR   ) DEBUG(8, ("d"));
157         if (result & aARCH  ) DEBUG(8, ("a"));
158         
159         DEBUG(8,("\n"));
160         return result;
161 }
162
163 /****************************************************************************
164  Get DOS attributes from an EA.
165 ****************************************************************************/
166
167 static BOOL get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
168 {
169         ssize_t sizeret;
170         fstring attrstr;
171         unsigned int dosattr;
172
173         if (!lp_store_dos_attributes(SNUM(conn))) {
174                 return False;
175         }
176
177         *pattr = 0;
178
179         sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
180         if (sizeret == -1) {
181 #if defined(ENOTSUP) && defined(ENOATTR)
182                 if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES)) {
183                         DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
184                                 path, strerror(errno) ));
185                 }
186 #endif
187                 return False;
188         }
189         /* Null terminate string. */
190         attrstr[sizeret] = 0;
191         DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
192
193         if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
194                         sscanf(attrstr, "%x", &dosattr) != 1) {
195                 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
196                 return False;
197         }
198
199         if (S_ISDIR(sbuf->st_mode)) {
200                 dosattr |= aDIR;
201         }
202         *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
203
204         DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
205
206         if (dosattr & aHIDDEN) DEBUG(8, ("h"));
207         if (dosattr & aRONLY ) DEBUG(8, ("r"));
208         if (dosattr & aSYSTEM) DEBUG(8, ("s"));
209         if (dosattr & aDIR   ) DEBUG(8, ("d"));
210         if (dosattr & aARCH  ) DEBUG(8, ("a"));
211         
212         DEBUG(8,("\n"));
213
214         return True;
215 }
216
217 /****************************************************************************
218  Set DOS attributes in an EA.
219 ****************************************************************************/
220
221 static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
222 {
223         fstring attrstr;
224         files_struct *fsp = NULL;
225         BOOL ret = False;
226
227         snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
228         if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
229                 if((errno != EPERM) && (errno != EACCES)) {
230                         return False;
231                 }
232
233                 /* We want DOS semantics, ie allow non owner with write permission to change the
234                         bits on a file. Just like file_utime below.
235                 */
236
237                 /* Check if we have write access. */
238                 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
239                         return False;
240
241                 /*
242                  * We need to open the file with write access whilst
243                  * still in our current user context. This ensures we
244                  * are not violating security in doing the setxattr.
245                  */
246
247                 fsp = open_file_fchmod(conn,path,sbuf);
248                 if (!fsp)
249                         return ret;
250                 become_root();
251                 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
252                         ret = True;
253                 }
254                 unbecome_root();
255                 close_file_fchmod(fsp);
256                 return ret;
257         }
258         DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
259         return True;
260 }
261
262 /****************************************************************************
263  Change a unix mode to a dos mode.
264 ****************************************************************************/
265
266 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
267 {
268         uint32 result = 0;
269
270         DEBUG(8,("dos_mode: %s\n", path));
271
272         if (!VALID_STAT(*sbuf)) {
273                 return 0;
274         }
275
276         /* Get the DOS attributes from an EA by preference. */
277         if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
278                 return result;
279         }
280
281         result = dos_mode_from_sbuf(conn, sbuf);
282
283         /* Now do any modifications that depend on the path name. */
284         /* hide files with a name starting with a . */
285         if (lp_hide_dot_files(SNUM(conn))) {
286                 const char *p = strrchr_m(path,'/');
287                 if (p)
288                         p++;
289                 else
290                         p = path;
291                 
292                 if (p[0] == '.' && p[1] != '.' && p[1] != 0)
293                         result |= aHIDDEN;
294         }
295         
296         /* Optimization : Only call is_hidden_path if it's not already
297            hidden. */
298         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
299                 result |= aHIDDEN;
300         }
301
302         DEBUG(8,("dos_mode returning "));
303
304         if (result & aHIDDEN) DEBUG(8, ("h"));
305         if (result & aRONLY ) DEBUG(8, ("r"));
306         if (result & aSYSTEM) DEBUG(8, ("s"));
307         if (result & aDIR   ) DEBUG(8, ("d"));
308         if (result & aARCH  ) DEBUG(8, ("a"));
309         
310         DEBUG(8,("\n"));
311
312         return(result);
313 }
314
315 /*******************************************************************
316  chmod a file - but preserve some bits.
317 ********************************************************************/
318
319 int file_set_dosmode(connection_struct *conn, const char *fname, uint32 dosmode, SMB_STRUCT_STAT *st)
320 {
321         SMB_STRUCT_STAT st1;
322         int mask=0;
323         mode_t tmp;
324         mode_t unixmode;
325         int ret = -1;
326
327         DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
328         if (!st) {
329                 st = &st1;
330                 if (SMB_VFS_STAT(conn,fname,st))
331                         return(-1);
332         }
333
334         get_acl_group_bits(conn, fname, &st->st_mode);
335
336         if (S_ISDIR(st->st_mode))
337                 dosmode |= aDIR;
338         else
339                 dosmode &= ~aDIR;
340
341         if (dos_mode(conn,fname,st) == dosmode)
342                 return(0);
343
344         /* Store the DOS attributes in an EA by preference. */
345         if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
346                 return 0;
347         }
348
349         unixmode = unix_mode(conn,dosmode,fname);
350
351         /* preserve the s bits */
352         mask |= (S_ISUID | S_ISGID);
353
354         /* preserve the t bit */
355 #ifdef S_ISVTX
356         mask |= S_ISVTX;
357 #endif
358
359         /* possibly preserve the x bits */
360         if (!MAP_ARCHIVE(conn))
361                 mask |= S_IXUSR;
362         if (!MAP_SYSTEM(conn))
363                 mask |= S_IXGRP;
364         if (!MAP_HIDDEN(conn))
365                 mask |= S_IXOTH;
366
367         unixmode |= (st->st_mode & mask);
368
369         /* if we previously had any r bits set then leave them alone */
370         if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
371                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
372                 unixmode |= tmp;
373         }
374
375         /* if we previously had any w bits set then leave them alone 
376                 whilst adding in the new w bits, if the new mode is not rdonly */
377         if (!IS_DOS_READONLY(dosmode)) {
378                 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
379         }
380
381         if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0)
382                 return 0;
383
384         if((errno != EPERM) && (errno != EACCES))
385                 return -1;
386
387         if(!lp_dos_filemode(SNUM(conn)))
388                 return -1;
389
390         /* We want DOS semantics, ie allow non owner with write permission to change the
391                 bits on a file. Just like file_utime below.
392         */
393
394         /* Check if we have write access. */
395         if (CAN_WRITE(conn)) {
396                 /*
397                  * We need to open the file with write access whilst
398                  * still in our current user context. This ensures we
399                  * are not violating security in doing the fchmod.
400                  * This file open does *not* break any oplocks we are
401                  * holding. We need to review this.... may need to
402                  * break batch oplocks open by others. JRA.
403                  */
404                 files_struct *fsp = open_file_fchmod(conn,fname,st);
405                 if (!fsp)
406                         return -1;
407                 become_root();
408                 ret = SMB_VFS_FCHMOD(fsp, fsp->fd, unixmode);
409                 unbecome_root();
410                 close_file_fchmod(fsp);
411         }
412
413         return( ret );
414 }
415
416 /*******************************************************************
417  Wrapper around dos_utime that possibly allows DOS semantics rather
418  than POSIX.
419 *******************************************************************/
420
421 int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
422 {
423         extern struct current_user current_user;
424         SMB_STRUCT_STAT sb;
425         int ret = -1;
426
427         errno = 0;
428
429         if(SMB_VFS_UTIME(conn,fname, times) == 0)
430                 return 0;
431
432         if((errno != EPERM) && (errno != EACCES))
433                 return -1;
434
435         if(!lp_dos_filetimes(SNUM(conn)))
436                 return -1;
437
438         /* We have permission (given by the Samba admin) to
439            break POSIX semantics and allow a user to change
440            the time on a file they don't own but can write to
441            (as DOS does).
442          */
443
444         if(SMB_VFS_STAT(conn,fname,&sb) != 0)
445                 return -1;
446
447         /* Check if we have write access. */
448         if (CAN_WRITE(conn)) {
449                 if (((sb.st_mode & S_IWOTH) || conn->admin_user ||
450                         ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) ||
451                         ((sb.st_mode & S_IWGRP) &&
452                                 in_group(sb.st_gid,current_user.gid,
453                                         current_user.ngroups,current_user.groups)))) {
454                         /* We are allowed to become root and change the filetime. */
455                         become_root();
456                         ret = SMB_VFS_UTIME(conn,fname, times);
457                         unbecome_root();
458                 }
459         }
460
461         return ret;
462 }
463   
464 /*******************************************************************
465  Change a filetime - possibly allowing DOS semantics.
466 *******************************************************************/
467
468 BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime)
469 {
470         struct utimbuf times;
471
472         if (null_mtime(mtime))
473                 return(True);
474
475         times.modtime = times.actime = mtime;
476
477         if (file_utime(conn, fname, &times)) {
478                 DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
479                 return False;
480         }
481   
482         return(True);
483