this completes the splitup of server.c.
[tprouty/samba.git] / source / smbd / dosmode.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    dos mode handling functions
5    Copyright (C) Andrew Tridgell 1992-1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 extern int DEBUGLEVEL;
25
26 /****************************************************************************
27   change a dos mode to a unix mode
28     base permission for files:
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          Then apply create mask,
35          then add force bits.
36     base permission for directories:
37          dos directory is represented in unix by unix's dir bit and the exec bit
38          Then apply create mask,
39          then add force bits.
40 ****************************************************************************/
41 mode_t unix_mode(connection_struct *conn,int dosmode)
42 {
43   mode_t result = (S_IRUSR | S_IRGRP | S_IROTH);
44
45   if ( !IS_DOS_READONLY(dosmode) )
46     result |= (S_IWUSR | S_IWGRP | S_IWOTH);
47  
48   if (IS_DOS_DIR(dosmode)) {
49     /* We never make directories read only for the owner as under DOS a user
50        can always create a file in a read-only directory. */
51     result |= (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH | S_IWUSR);
52     /* Apply directory mask */
53     result &= lp_dir_mode(SNUM(conn));
54     /* Add in force bits */
55     result |= lp_force_dir_mode(SNUM(conn));
56   } else { 
57     if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
58       result |= S_IXUSR;
59
60     if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
61       result |= S_IXGRP;
62  
63     if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
64       result |= S_IXOTH;  
65  
66     /* Apply mode mask */
67     result &= lp_create_mode(SNUM(conn));
68     /* Add in force bits */
69     result |= lp_force_create_mode(SNUM(conn));
70   }
71   return(result);
72 }
73
74
75 /****************************************************************************
76   change a unix mode to a dos mode
77 ****************************************************************************/
78 int dos_mode(connection_struct *conn,char *path,struct stat *sbuf)
79 {
80   int result = 0;
81   extern struct current_user current_user;
82
83   DEBUG(8,("dos_mode: %s\n", path));
84
85   if (CAN_WRITE(conn) && !lp_alternate_permissions(SNUM(conn))) {
86     if (!((sbuf->st_mode & S_IWOTH) ||
87           conn->admin_user ||
88           ((sbuf->st_mode & S_IWUSR) && current_user.uid==sbuf->st_uid) ||
89           ((sbuf->st_mode & S_IWGRP) && 
90            in_group(sbuf->st_gid,current_user.gid,
91                     current_user.ngroups,current_user.groups))))
92       result |= aRONLY;
93   } else {
94     if ((sbuf->st_mode & S_IWUSR) == 0)
95       result |= aRONLY;
96   }
97
98   if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
99     result |= aARCH;
100
101   if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
102     result |= aSYSTEM;
103
104   if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
105     result |= aHIDDEN;   
106   
107   if (S_ISDIR(sbuf->st_mode))
108     result = aDIR | (result & aRONLY);
109
110 #ifdef S_ISLNK
111 #if LINKS_READ_ONLY
112   if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
113     result |= aRONLY;
114 #endif
115 #endif
116
117   /* hide files with a name starting with a . */
118   if (lp_hide_dot_files(SNUM(conn)))
119     {
120       char *p = strrchr(path,'/');
121       if (p)
122         p++;
123       else
124         p = path;
125       
126       if (p[0] == '.' && p[1] != '.' && p[1] != 0)
127         result |= aHIDDEN;
128     }
129
130   /* Optimization : Only call is_hidden_path if it's not already
131      hidden. */
132   if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path))
133   {
134     result |= aHIDDEN;
135   }
136
137   DEBUG(8,("dos_mode returning "));
138
139   if (result & aHIDDEN) DEBUG(8, ("h"));
140   if (result & aRONLY ) DEBUG(8, ("r"));
141   if (result & aSYSTEM) DEBUG(8, ("s"));
142   if (result & aDIR   ) DEBUG(8, ("d"));
143   if (result & aARCH  ) DEBUG(8, ("a"));
144
145   DEBUG(8,("\n"));
146
147   return(result);
148 }
149
150 /*******************************************************************
151 chmod a file - but preserve some bits
152 ********************************************************************/
153 int dos_chmod(connection_struct *conn,char *fname,int dosmode,struct stat *st)
154 {
155   struct stat st1;
156   int mask=0;
157   int tmp;
158   int unixmode;
159
160   if (!st) {
161     st = &st1;
162     if (sys_stat(fname,st)) return(-1);
163   }
164
165   if (S_ISDIR(st->st_mode)) dosmode |= aDIR;
166
167   if (dos_mode(conn,fname,st) == dosmode) return(0);
168
169   unixmode = unix_mode(conn,dosmode);
170
171   /* preserve the s bits */
172   mask |= (S_ISUID | S_ISGID);
173
174   /* preserve the t bit */
175 #ifdef S_ISVTX
176   mask |= S_ISVTX;
177 #endif
178
179   /* possibly preserve the x bits */
180   if (!MAP_ARCHIVE(conn)) mask |= S_IXUSR;
181   if (!MAP_SYSTEM(conn)) mask |= S_IXGRP;
182   if (!MAP_HIDDEN(conn)) mask |= S_IXOTH;
183
184   unixmode |= (st->st_mode & mask);
185
186   /* if we previously had any r bits set then leave them alone */
187   if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
188     unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
189     unixmode |= tmp;
190   }
191
192   /* if we previously had any w bits set then leave them alone 
193    if the new mode is not rdonly */
194   if (!IS_DOS_READONLY(dosmode) &&
195       (tmp = st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))) {
196     unixmode &= ~(S_IWUSR|S_IWGRP|S_IWOTH);
197     unixmode |= tmp;
198   }
199
200   return(sys_chmod(fname,unixmode));
201 }
202
203
204 /*******************************************************************
205 Wrapper around sys_utime that possibly allows DOS semantics rather
206 than POSIX.
207 *******************************************************************/
208 int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
209 {
210   extern struct current_user current_user;
211   struct stat sb;
212   int ret = -1;
213
214   errno = 0;
215
216   if(sys_utime(fname, times) == 0)
217     return 0;
218
219   if((errno != EPERM) && (errno != EACCES))
220     return -1;
221
222   if(!lp_dos_filetimes(SNUM(conn)))
223     return -1;
224
225   /* We have permission (given by the Samba admin) to
226      break POSIX semantics and allow a user to change
227      the time on a file they don't own but can write to
228      (as DOS does).
229    */
230
231   if(sys_stat(fname,&sb) != 0)
232     return -1;
233
234   /* Check if we have write access. */
235   if (CAN_WRITE(conn)) {
236           if (((sb.st_mode & S_IWOTH) ||
237                conn->admin_user ||
238                ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) ||
239                ((sb.st_mode & S_IWGRP) &&
240                 in_group(sb.st_gid,current_user.gid,
241                          current_user.ngroups,current_user.groups)))) {
242                   /* We are allowed to become root and change the filetime. */
243                   become_root(False);
244                   ret = sys_utime(fname, times);
245                   unbecome_root(False);
246           }
247   }
248
249   return ret;
250 }
251   
252 /*******************************************************************
253 Change a filetime - possibly allowing DOS semantics.
254 *******************************************************************/
255 BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime)
256 {
257   struct utimbuf times;
258
259   if (null_mtime(mtime)) return(True);
260
261   times.modtime = times.actime = mtime;
262
263   if (file_utime(conn, fname, &times)) {
264     DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
265   }
266   
267   return(True);
268
269
270