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