first pass at updating head branch to be to be the same as the SAMBA_2_0 branch
[kai/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_mask(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_mask(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,SMB_STRUCT_STAT *sbuf)
79 {
80   int result = 0;
81
82   DEBUG(8,("dos_mode: %s\n", path));
83
84   if ((sbuf->st_mode & S_IWUSR) == 0)
85       result |= aRONLY;
86
87   if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
88     result |= aARCH;
89
90   if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
91     result |= aSYSTEM;
92
93   if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
94     result |= aHIDDEN;   
95   
96   if (S_ISDIR(sbuf->st_mode))
97     result = aDIR | (result & aRONLY);
98
99 #ifdef S_ISLNK
100 #if LINKS_READ_ONLY
101   if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
102     result |= aRONLY;
103 #endif
104 #endif
105
106   /* hide files with a name starting with a . */
107   if (lp_hide_dot_files(SNUM(conn)))
108     {
109       char *p = strrchr(path,'/');
110       if (p)
111         p++;
112       else
113         p = path;
114       
115       if (p[0] == '.' && p[1] != '.' && p[1] != 0)
116         result |= aHIDDEN;
117     }
118
119   /* Optimization : Only call is_hidden_path if it's not already
120      hidden. */
121   if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path))
122   {
123     result |= aHIDDEN;
124   }
125
126   DEBUG(8,("dos_mode returning "));
127
128   if (result & aHIDDEN) DEBUG(8, ("h"));
129   if (result & aRONLY ) DEBUG(8, ("r"));
130   if (result & aSYSTEM) DEBUG(8, ("s"));
131   if (result & aDIR   ) DEBUG(8, ("d"));
132   if (result & aARCH  ) DEBUG(8, ("a"));
133
134   DEBUG(8,("\n"));
135
136   return(result);
137 }
138
139 /*******************************************************************
140 chmod a file - but preserve some bits
141 ********************************************************************/
142 int file_chmod(connection_struct *conn,char *fname,int dosmode,SMB_STRUCT_STAT *st)
143 {
144   SMB_STRUCT_STAT st1;
145   int mask=0;
146   mode_t tmp;
147   mode_t unixmode;
148
149   if (!st) {
150     st = &st1;
151     if (dos_stat(fname,st)) return(-1);
152   }
153
154   if (S_ISDIR(st->st_mode)) dosmode |= aDIR;
155
156   if (dos_mode(conn,fname,st) == dosmode) return(0);
157
158   unixmode = unix_mode(conn,dosmode);
159
160   /* preserve the s bits */
161   mask |= (S_ISUID | S_ISGID);
162
163   /* preserve the t bit */
164 #ifdef S_ISVTX
165   mask |= S_ISVTX;
166 #endif
167
168   /* possibly preserve the x bits */
169   if (!MAP_ARCHIVE(conn)) mask |= S_IXUSR;
170   if (!MAP_SYSTEM(conn)) mask |= S_IXGRP;
171   if (!MAP_HIDDEN(conn)) mask |= S_IXOTH;
172
173   unixmode |= (st->st_mode & mask);
174
175   /* if we previously had any r bits set then leave them alone */
176   if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
177     unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
178     unixmode |= tmp;
179   }
180
181   /* if we previously had any w bits set then leave them alone 
182    whilst adding in the new w bits, if the new mode is not rdonly */
183   if (!IS_DOS_READONLY(dosmode)) {
184     unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
185   }
186
187   return(dos_chmod(fname,unixmode));
188 }
189
190
191 /*******************************************************************
192 Wrapper around dos_utime that possibly allows DOS semantics rather
193 than POSIX.
194 *******************************************************************/
195 int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
196 {
197   extern struct current_user current_user;
198   SMB_STRUCT_STAT sb;
199   int ret = -1;
200
201   errno = 0;
202
203   if(dos_utime(fname, times) == 0)
204     return 0;
205
206   if((errno != EPERM) && (errno != EACCES))
207     return -1;
208
209   if(!lp_dos_filetimes(SNUM(conn)))
210     return -1;
211
212   /* We have permission (given by the Samba admin) to
213      break POSIX semantics and allow a user to change
214      the time on a file they don't own but can write to
215      (as DOS does).
216    */
217
218   if(dos_stat(fname,&sb) != 0)
219     return -1;
220
221   /* Check if we have write access. */
222   if (CAN_WRITE(conn)) {
223           if (((sb.st_mode & S_IWOTH) ||
224                conn->admin_user ||
225                ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) ||
226                ((sb.st_mode & S_IWGRP) &&
227                 in_group(sb.st_gid,current_user.gid,
228                          current_user.ngroups,current_user.groups)))) {
229                   /* We are allowed to become root and change the filetime. */
230                   become_root(False);
231                   ret = dos_utime(fname, times);
232                   unbecome_root(False);
233           }
234   }
235
236   return ret;
237 }
238   
239 /*******************************************************************
240 Change a filetime - possibly allowing DOS semantics.
241 *******************************************************************/
242 BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime)
243 {
244   struct utimbuf times;
245
246   if (null_mtime(mtime)) return(True);
247
248   times.modtime = times.actime = mtime;
249
250   if (file_utime(conn, fname, &times)) {
251     DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
252     return False;
253   }
254   
255   return(True);
256