Get the logic right thistime :-(.
[samba.git] / source3 / smbd / dosmode.c
1 #define OLD_NTDOMAIN 1
2
3 /* 
4    Unix SMB/Netbios implementation.
5    Version 1.9.
6    dos mode handling functions
7    Copyright (C) Andrew Tridgell 1992-1998
8    
9    This program is free software; you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation; either version 2 of the License, or
12    (at your option) any later version.
13    
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18    
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23
24 #include "includes.h"
25
26 extern int DEBUGLEVEL;
27
28 /****************************************************************************
29   change a dos mode to a unix mode
30     base permission for files:
31          if inheriting
32            apply read/write bits from parent directory.
33          else   
34            everybody gets read bit set
35          dos readonly is represented in unix by removing everyone's write bit
36          dos archive is represented in unix by the user's execute bit
37          dos system is represented in unix by the group's execute bit
38          dos hidden is represented in unix by the other's execute bit
39          if !inheriting {
40            Then apply create mask,
41            then add force bits.
42          }
43     base permission for directories:
44          dos directory is represented in unix by unix's dir bit and the exec bit
45          if !inheriting {
46            Then apply create mask,
47            then add force bits.
48          }
49 ****************************************************************************/
50 mode_t unix_mode(connection_struct *conn,int dosmode,const char *fname)
51 {
52   mode_t result = (S_IRUSR | S_IRGRP | S_IROTH);
53   mode_t dir_mode = 0; /* Mode of the parent directory if inheriting. */
54
55   if ( !IS_DOS_READONLY(dosmode) )
56     result |= (S_IWUSR | S_IWGRP | S_IWOTH);
57
58   if (fname && lp_inherit_perms(SNUM(conn))) {
59     char *dname;
60     SMB_STRUCT_STAT sbuf;
61
62     dname = parent_dirname(fname);
63     DEBUG(2,("unix_mode(%s) inheriting from %s\n",fname,dname));
64     if (vfs_stat(conn,dname,&sbuf) != 0) {
65       DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",fname,dname,strerror(errno)));
66       return(0);      /* *** shouldn't happen! *** */
67     }
68
69     /* Save for later - but explicitly remove setuid bit for safety. */
70     dir_mode = sbuf.st_mode & ~S_ISUID;
71     DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
72     /* Clear "result" */
73     result = 0;
74   } 
75
76   if (IS_DOS_DIR(dosmode)) {
77     /* We never make directories read only for the owner as under DOS a user
78        can always create a file in a read-only directory. */
79     result |= (S_IFDIR | S_IWUSR);
80
81     if (dir_mode) {
82       /* Inherit mode of parent directory. */
83       result |= dir_mode;
84     } else {
85       /* Provisionally add all 'x' bits */
86       result |= (S_IXUSR | S_IXGRP | S_IXOTH);                 
87
88       /* Apply directory mask */
89       result &= lp_dir_mask(SNUM(conn));
90       /* Add in force bits */
91       result |= lp_force_dir_mode(SNUM(conn));
92     }
93   } else { 
94     if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
95       result |= S_IXUSR;
96
97     if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
98       result |= S_IXGRP;
99  
100     if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
101       result |= S_IXOTH;  
102
103     if (dir_mode) {
104       /* Inherit 666 component of parent directory mode */
105       result |= dir_mode
106         &  (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
107     } else {
108       /* Apply mode mask */
109       result &= lp_create_mask(SNUM(conn));
110       /* Add in force bits */
111       result |= lp_force_create_mode(SNUM(conn));
112     }
113   }
114   return(result);
115 }
116
117
118 /****************************************************************************
119   change a unix mode to a dos mode
120 ****************************************************************************/
121 int dos_mode(connection_struct *conn,char *path,SMB_STRUCT_STAT *sbuf)
122 {
123   int result = 0;
124
125   DEBUG(8,("dos_mode: %s\n", path));
126
127   if ((sbuf->st_mode & S_IWUSR) == 0)
128       result |= aRONLY;
129
130   if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
131     result |= aARCH;
132
133   if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
134     result |= aSYSTEM;
135
136   if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
137     result |= aHIDDEN;   
138   
139   if (S_ISDIR(sbuf->st_mode))
140     result = aDIR | (result & aRONLY);
141  
142 #ifdef S_ISLNK
143 #if LINKS_READ_ONLY
144   if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
145     result |= aRONLY;
146 #endif
147 #endif
148
149   /* hide files with a name starting with a . */
150   if (lp_hide_dot_files(SNUM(conn)))
151     {
152       char *p = strrchr(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   {
166     result |= aHIDDEN;
167   }
168
169   DEBUG(8,("dos_mode returning "));
170
171   if (result & aHIDDEN) DEBUG(8, ("h"));
172   if (result & aRONLY ) DEBUG(8, ("r"));
173   if (result & aSYSTEM) DEBUG(8, ("s"));
174   if (result & aDIR   ) DEBUG(8, ("d"));
175   if (result & aARCH  ) DEBUG(8, ("a"));
176
177   DEBUG(8,("\n"));
178
179   return(result);
180 }
181
182 /*******************************************************************
183 chmod a file - but preserve some bits
184 ********************************************************************/
185 int file_chmod(connection_struct *conn,char *fname,int dosmode,SMB_STRUCT_STAT *st)
186 {
187         extern struct current_user current_user;
188         SMB_STRUCT_STAT st1;
189         int mask=0;
190         mode_t tmp;
191         mode_t unixmode;
192         int ret = -1;
193
194         if (!st) {
195                 st = &st1;
196                 if (vfs_stat(conn,fname,st))
197                         return(-1);
198         }
199
200         if (S_ISDIR(st->st_mode))
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                 if (((st->st_mode & S_IWOTH) ||
254                                 conn->admin_user ||
255                                 ((st->st_mode & S_IWUSR) && current_user.uid==st->st_uid) ||
256                                 ((st->st_mode & S_IWGRP) &&
257                                 in_group(st->st_gid,current_user.gid, current_user.ngroups,current_user.groups)))) {
258                                         /* We are allowed to become root and change the file mode. */
259                                         become_root();
260                                         ret = vfs_chmod(conn,fname,unixmode);
261                                         unbecome_root();
262                 }
263         }
264
265         return( ret );
266 }
267
268
269 /*******************************************************************
270 Wrapper around dos_utime that possibly allows DOS semantics rather
271 than POSIX.
272 *******************************************************************/
273 int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
274 {
275   extern struct current_user current_user;
276   SMB_STRUCT_STAT sb;
277   int ret = -1;
278
279   errno = 0;
280
281   if(conn->vfs_ops.utime(conn,dos_to_unix(fname, False), times) == 0)
282     return 0;
283
284   if((errno != EPERM) && (errno != EACCES))
285     return -1;
286
287   if(!lp_dos_filetimes(SNUM(conn)))
288     return -1;
289
290   /* We have permission (given by the Samba admin) to
291      break POSIX semantics and allow a user to change
292      the time on a file they don't own but can write to
293      (as DOS does).
294    */
295
296   if(vfs_stat(conn,fname,&sb) != 0)
297     return -1;
298
299   /* Check if we have write access. */
300   if (CAN_WRITE(conn)) {
301           if (((sb.st_mode & S_IWOTH) ||
302                conn->admin_user ||
303                ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) ||
304                ((sb.st_mode & S_IWGRP) &&
305                 in_group(sb.st_gid,current_user.gid,
306                          current_user.ngroups,current_user.groups)))) {
307                   /* We are allowed to become root and change the filetime. */
308                   become_root();
309                   ret = conn->vfs_ops.utime(conn,dos_to_unix(fname, False), times);
310                   unbecome_root();
311           }
312   }
313
314   return ret;
315 }
316   
317 /*******************************************************************
318 Change a filetime - possibly allowing DOS semantics.
319 *******************************************************************/
320 BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime)
321 {
322   struct utimbuf times;
323
324   if (null_mtime(mtime)) return(True);
325
326   times.modtime = times.actime = mtime;
327
328   if (file_utime(conn, fname, &times)) {
329     DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
330     return False;
331   }
332   
333   return(True);
334
335
336 #undef OLD_NTDOMAIN