1369c46b2f6245d3fa64224029ea10b7d949feff
[nivanova/samba-autobuild/.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);
49         mode_t dir_mode = 0; /* Mode of the parent directory if inheriting. */
50
51         if ( !IS_DOS_READONLY(dosmode) )
52                 result |= (S_IWUSR | S_IWGRP | S_IWOTH);
53
54         if (fname && lp_inherit_perms(SNUM(conn))) {
55                 char *dname;
56                 SMB_STRUCT_STAT sbuf;
57
58                 dname = parent_dirname(fname);
59                 DEBUG(2,("unix_mode(%s) inheriting from %s\n",fname,dname));
60                 if (SMB_VFS_STAT(conn,dname,&sbuf) != 0) {
61                         DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",fname,dname,strerror(errno)));
62                         return(0);      /* *** shouldn't happen! *** */
63                 }
64
65                 /* Save for later - but explicitly remove setuid bit for safety. */
66                 dir_mode = sbuf.st_mode & ~S_ISUID;
67                 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
68                 /* Clear "result" */
69                 result = 0;
70         } 
71
72         if (IS_DOS_DIR(dosmode)) {
73                 /* We never make directories read only for the owner as under DOS a user
74                 can always create a file in a read-only directory. */
75                 result |= (S_IFDIR | S_IWUSR);
76
77                 if (dir_mode) {
78                         /* Inherit mode of parent directory. */
79                         result |= dir_mode;
80                 } else {
81                         /* Provisionally add all 'x' bits */
82                         result |= (S_IXUSR | S_IXGRP | S_IXOTH);                 
83
84                         /* Apply directory mask */
85                         result &= lp_dir_mask(SNUM(conn));
86                         /* Add in force bits */
87                         result |= lp_force_dir_mode(SNUM(conn));
88                 }
89         } else { 
90                 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
91                         result |= S_IXUSR;
92
93                 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
94                         result |= S_IXGRP;
95  
96                 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
97                         result |= S_IXOTH;  
98
99                 if (dir_mode) {
100                         /* Inherit 666 component of parent directory mode */
101                         result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
102                 } else {
103                         /* Apply mode mask */
104                         result &= lp_create_mask(SNUM(conn));
105                         /* Add in force bits */
106                         result |= lp_force_create_mode(SNUM(conn));
107                 }
108         }
109
110         DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
111         return(result);
112 }
113
114 /****************************************************************************
115  Change a unix mode to a dos mode.
116 ****************************************************************************/
117
118 uint32 dos_mode_from_sbuf(connection_struct *conn, SMB_STRUCT_STAT *sbuf)
119 {
120         int result = 0;
121
122         if ((sbuf->st_mode & S_IWUSR) == 0)
123                 result |= aRONLY;
124         
125         if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
126                 result |= aARCH;
127
128         if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
129                 result |= aSYSTEM;
130         
131         if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
132                 result |= aHIDDEN;   
133   
134         if (S_ISDIR(sbuf->st_mode))
135                 result = aDIR | (result & aRONLY);
136
137 #if defined (HAVE_STAT_ST_BLOCKS) && defined (HAVE_STAT_ST_BLKSIZE)
138         if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)sbuf->st_blksize) {
139                 result |= FILE_ATTRIBUTE_SPARSE;
140         }
141 #endif
142  
143 #ifdef S_ISLNK
144 #if LINKS_READ_ONLY
145         if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
146                 result |= aRONLY;
147 #endif
148 #endif
149
150         DEBUG(8,("dos_mode_from_sbuf returning "));
151
152         if (result & aHIDDEN) DEBUG(8, ("h"));
153         if (result & aRONLY ) DEBUG(8, ("r"));
154         if (result & aSYSTEM) DEBUG(8, ("s"));
155         if (result & aDIR   ) DEBUG(8, ("d"));
156         if (result & aARCH  ) DEBUG(8, ("a"));
157         
158         DEBUG(8,("\n"));
159         return result;
160 }
161
162 /****************************************************************************
163  Change a unix mode to a dos mode.
164 ****************************************************************************/
165
166 uint32 dos_mode(connection_struct *conn,char *path,SMB_STRUCT_STAT *sbuf)
167 {
168         int result = 0;
169
170         DEBUG(8,("dos_mode: %s\n", path));
171
172         result = dos_mode_from_sbuf(conn, sbuf);
173
174         /* Now do any modifications that depend on the path name. */
175         /* hide files with a name starting with a . */
176         if (lp_hide_dot_files(SNUM(conn))) {
177                 char *p = strrchr_m(path,'/');
178                 if (p)
179                         p++;
180                 else
181                         p = path;
182                 
183                 if (p[0] == '.' && p[1] != '.' && p[1] != 0)
184                         result |= aHIDDEN;
185         }
186         
187         /* Optimization : Only call is_hidden_path if it's not already
188            hidden. */
189         if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
190                 result |= aHIDDEN;
191         }
192
193         DEBUG(8,("dos_mode returning "));
194
195         if (result & aHIDDEN) DEBUG(8, ("h"));
196         if (result & aRONLY ) DEBUG(8, ("r"));
197         if (result & aSYSTEM) DEBUG(8, ("s"));
198         if (result & aDIR   ) DEBUG(8, ("d"));
199         if (result & aARCH  ) DEBUG(8, ("a"));
200         
201         DEBUG(8,("\n"));
202
203         return(result);
204 }
205
206 /*******************************************************************
207  chmod a file - but preserve some bits.
208 ********************************************************************/
209
210 int file_chmod(connection_struct *conn,char *fname, uint32 dosmode,SMB_STRUCT_STAT *st)
211 {
212         SMB_STRUCT_STAT st1;
213         int mask=0;
214         mode_t tmp;
215         mode_t unixmode;
216         int ret = -1;
217
218         if (!st) {
219                 st = &st1;
220                 if (SMB_VFS_STAT(conn,fname,st))
221                         return(-1);
222         }
223
224         get_acl_group_bits(conn, fname, &st->st_mode);
225
226         if (S_ISDIR(st->st_mode))
227                 dosmode |= aDIR;
228         else
229                 dosmode &= ~aDIR;
230
231         if (dos_mode(conn,fname,st) == dosmode)
232                 return(0);
233
234         unixmode = unix_mode(conn,dosmode,fname);
235
236         /* preserve the s bits */
237         mask |= (S_ISUID | S_ISGID);
238
239         /* preserve the t bit */
240 #ifdef S_ISVTX
241         mask |= S_ISVTX;
242 #endif
243
244         /* possibly preserve the x bits */
245         if (!MAP_ARCHIVE(conn))
246                 mask |= S_IXUSR;
247         if (!MAP_SYSTEM(conn))
248                 mask |= S_IXGRP;
249         if (!MAP_HIDDEN(conn))
250                 mask |= S_IXOTH;
251
252         unixmode |= (st->st_mode & mask);
253
254         /* if we previously had any r bits set then leave them alone */
255         if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
256                 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
257                 unixmode |= tmp;
258         }
259
260         /* if we previously had any w bits set then leave them alone 
261                 whilst adding in the new w bits, if the new mode is not rdonly */
262         if (!IS_DOS_READONLY(dosmode)) {
263                 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
264         }
265
266         if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0)
267                 return 0;
268
269         if((errno != EPERM) && (errno != EACCES))
270                 return -1;
271
272         if(!lp_dos_filemode(SNUM(conn)))
273                 return -1;
274
275         /* We want DOS semantics, ie allow non owner with write permission to change the
276                 bits on a file. Just like file_utime below.
277         */
278
279         /* Check if we have write access. */
280         if (CAN_WRITE(conn)) {
281                 /*
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 fchmod.
285                  * This file open does *not* break any oplocks we are
286                  * holding. We need to review this.... may need to
287                  * break batch oplocks open by others. JRA.
288                  */
289                 files_struct *fsp = open_file_fchmod(conn,fname,st);
290                 if (!fsp)
291                         return -1;
292                 become_root();
293                 ret = SMB_VFS_FCHMOD(fsp, fsp->fd, unixmode);
294                 unbecome_root();
295                 close_file_fchmod(fsp);
296         }
297
298         return( ret );
299 }
300
301 /*******************************************************************
302  Wrapper around dos_utime that possibly allows DOS semantics rather
303  than POSIX.
304 *******************************************************************/
305
306 int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
307 {
308         extern struct current_user current_user;
309         SMB_STRUCT_STAT sb;
310         int ret = -1;
311
312         errno = 0;
313
314         if(SMB_VFS_UTIME(conn,fname, times) == 0)
315                 return 0;
316
317         if((errno != EPERM) && (errno != EACCES))
318                 return -1;
319
320         if(!lp_dos_filetimes(SNUM(conn)))
321                 return -1;
322
323         /* We have permission (given by the Samba admin) to
324            break POSIX semantics and allow a user to change
325            the time on a file they don't own but can write to
326            (as DOS does).
327          */
328
329         if(SMB_VFS_STAT(conn,fname,&sb) != 0)
330                 return -1;
331
332         /* Check if we have write access. */
333         if (CAN_WRITE(conn)) {
334                 if (((sb.st_mode & S_IWOTH) || conn->admin_user ||
335                         ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) ||
336                         ((sb.st_mode & S_IWGRP) &&
337                                 in_group(sb.st_gid,current_user.gid,
338                                         current_user.ngroups,current_user.groups)))) {
339                         /* We are allowed to become root and change the filetime. */
340                         become_root();
341                         ret = SMB_VFS_UTIME(conn,fname, times);
342                         unbecome_root();
343                 }
344         }
345
346         return ret;
347 }
348   
349 /*******************************************************************
350  Change a filetime - possibly allowing DOS semantics.
351 *******************************************************************/
352
353 BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime)
354 {
355         struct utimbuf times;
356
357         if (null_mtime(mtime))
358                 return(True);
359
360         times.modtime = times.actime = mtime;
361
362         if (file_utime(conn, fname, &times)) {
363                 DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
364                 return False;
365         }
366   
367         return(True);
368