r15: syncing for 3.0.3pre2
[Samba.git] / source / smbd / dosmode.c
blobd7dc63bb2fd746a0092481b9facde94c2b40da5f
1 /*
2 Unix SMB/CIFS implementation.
3 dos mode handling functions
4 Copyright (C) Andrew Tridgell 1992-1998
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.
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.
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.
21 #include "includes.h"
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.
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.
44 ****************************************************************************/
46 mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname)
48 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
49 mode_t dir_mode = 0; /* Mode of the parent directory if inheriting. */
51 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
52 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
55 if (fname && lp_inherit_perms(SNUM(conn))) {
56 char *dname;
57 SMB_STRUCT_STAT sbuf;
59 dname = parent_dirname(fname);
60 DEBUG(2,("unix_mode(%s) inheriting from %s\n",fname,dname));
61 if (SMB_VFS_STAT(conn,dname,&sbuf) != 0) {
62 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",fname,dname,strerror(errno)));
63 return(0); /* *** shouldn't happen! *** */
66 /* Save for later - but explicitly remove setuid bit for safety. */
67 dir_mode = sbuf.st_mode & ~S_ISUID;
68 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
69 /* Clear "result" */
70 result = 0;
73 if (IS_DOS_DIR(dosmode)) {
74 /* We never make directories read only for the owner as under DOS a user
75 can always create a file in a read-only directory. */
76 result |= (S_IFDIR | S_IWUSR);
78 if (dir_mode) {
79 /* Inherit mode of parent directory. */
80 result |= dir_mode;
81 } else {
82 /* Provisionally add all 'x' bits */
83 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
85 /* Apply directory mask */
86 result &= lp_dir_mask(SNUM(conn));
87 /* Add in force bits */
88 result |= lp_force_dir_mode(SNUM(conn));
90 } else {
91 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
92 result |= S_IXUSR;
94 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
95 result |= S_IXGRP;
97 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
98 result |= S_IXOTH;
100 if (dir_mode) {
101 /* Inherit 666 component of parent directory mode */
102 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
103 } else {
104 /* Apply mode mask */
105 result &= lp_create_mask(SNUM(conn));
106 /* Add in force bits */
107 result |= lp_force_create_mode(SNUM(conn));
111 DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
112 return(result);
115 /****************************************************************************
116 Change a unix mode to a dos mode.
117 ****************************************************************************/
119 uint32 dos_mode_from_sbuf(connection_struct *conn, SMB_STRUCT_STAT *sbuf)
121 int result = 0;
123 if ((sbuf->st_mode & S_IWUSR) == 0)
124 result |= aRONLY;
126 if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
127 result |= aARCH;
129 if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
130 result |= aSYSTEM;
132 if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
133 result |= aHIDDEN;
135 if (S_ISDIR(sbuf->st_mode))
136 result = aDIR | (result & aRONLY);
138 #if defined (HAVE_STAT_ST_BLOCKS) && defined (HAVE_STAT_ST_BLKSIZE)
139 if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)sbuf->st_blksize) {
140 result |= FILE_ATTRIBUTE_SPARSE;
142 #endif
144 #ifdef S_ISLNK
145 #if LINKS_READ_ONLY
146 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
147 result |= aRONLY;
148 #endif
149 #endif
151 DEBUG(8,("dos_mode_from_sbuf returning "));
153 if (result & aHIDDEN) DEBUG(8, ("h"));
154 if (result & aRONLY ) DEBUG(8, ("r"));
155 if (result & aSYSTEM) DEBUG(8, ("s"));
156 if (result & aDIR ) DEBUG(8, ("d"));
157 if (result & aARCH ) DEBUG(8, ("a"));
159 DEBUG(8,("\n"));
160 return result;
163 /****************************************************************************
164 Get DOS attributes from an EA.
165 ****************************************************************************/
167 static BOOL get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
169 ssize_t sizeret;
170 fstring attrstr;
171 unsigned int dosattr;
173 if (!lp_store_dos_attributes(SNUM(conn))) {
174 return False;
177 *pattr = 0;
179 sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
180 if (sizeret == -1) {
181 #if defined(ENOTSUP) && defined(ENOATTR)
182 if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES)) {
183 DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
184 path, strerror(errno) ));
186 #endif
187 return False;
189 /* Null terminate string. */
190 attrstr[sizeret] = 0;
191 DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
193 if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
194 sscanf(attrstr, "%x", &dosattr) != 1) {
195 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
196 return False;
199 if (S_ISDIR(sbuf->st_mode)) {
200 dosattr |= aDIR;
202 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
204 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
206 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
207 if (dosattr & aRONLY ) DEBUG(8, ("r"));
208 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
209 if (dosattr & aDIR ) DEBUG(8, ("d"));
210 if (dosattr & aARCH ) DEBUG(8, ("a"));
212 DEBUG(8,("\n"));
214 return True;
217 /****************************************************************************
218 Set DOS attributes in an EA.
219 ****************************************************************************/
221 static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
223 fstring attrstr;
224 files_struct *fsp = NULL;
225 BOOL ret = False;
227 snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
228 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
229 if((errno != EPERM) && (errno != EACCES)) {
230 return False;
233 /* We want DOS semantics, ie allow non owner with write permission to change the
234 bits on a file. Just like file_utime below.
237 /* Check if we have write access. */
238 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
239 return False;
242 * We need to open the file with write access whilst
243 * still in our current user context. This ensures we
244 * are not violating security in doing the setxattr.
247 fsp = open_file_fchmod(conn,path,sbuf);
248 if (!fsp)
249 return ret;
250 become_root();
251 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
252 ret = True;
254 unbecome_root();
255 close_file_fchmod(fsp);
256 return ret;
258 DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
259 return True;
262 /****************************************************************************
263 Change a unix mode to a dos mode.
264 ****************************************************************************/
266 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
268 uint32 result = 0;
270 DEBUG(8,("dos_mode: %s\n", path));
272 if (!VALID_STAT(*sbuf)) {
273 return 0;
276 /* Get the DOS attributes from an EA by preference. */
277 if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
278 return result;
281 result = dos_mode_from_sbuf(conn, sbuf);
283 /* Now do any modifications that depend on the path name. */
284 /* hide files with a name starting with a . */
285 if (lp_hide_dot_files(SNUM(conn))) {
286 const char *p = strrchr_m(path,'/');
287 if (p)
288 p++;
289 else
290 p = path;
292 if (p[0] == '.' && p[1] != '.' && p[1] != 0)
293 result |= aHIDDEN;
296 /* Optimization : Only call is_hidden_path if it's not already
297 hidden. */
298 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
299 result |= aHIDDEN;
302 DEBUG(8,("dos_mode returning "));
304 if (result & aHIDDEN) DEBUG(8, ("h"));
305 if (result & aRONLY ) DEBUG(8, ("r"));
306 if (result & aSYSTEM) DEBUG(8, ("s"));
307 if (result & aDIR ) DEBUG(8, ("d"));
308 if (result & aARCH ) DEBUG(8, ("a"));
310 DEBUG(8,("\n"));
312 return(result);
315 /*******************************************************************
316 chmod a file - but preserve some bits.
317 ********************************************************************/
319 int file_set_dosmode(connection_struct *conn, const char *fname, uint32 dosmode, SMB_STRUCT_STAT *st)
321 SMB_STRUCT_STAT st1;
322 int mask=0;
323 mode_t tmp;
324 mode_t unixmode;
325 int ret = -1;
327 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
328 if (!st) {
329 st = &st1;
330 if (SMB_VFS_STAT(conn,fname,st))
331 return(-1);
334 get_acl_group_bits(conn, fname, &st->st_mode);
336 if (S_ISDIR(st->st_mode))
337 dosmode |= aDIR;
338 else
339 dosmode &= ~aDIR;
341 if (dos_mode(conn,fname,st) == dosmode)
342 return(0);
344 /* Store the DOS attributes in an EA by preference. */
345 if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
346 return 0;
349 unixmode = unix_mode(conn,dosmode,fname);
351 /* preserve the s bits */
352 mask |= (S_ISUID | S_ISGID);
354 /* preserve the t bit */
355 #ifdef S_ISVTX
356 mask |= S_ISVTX;
357 #endif
359 /* possibly preserve the x bits */
360 if (!MAP_ARCHIVE(conn))
361 mask |= S_IXUSR;
362 if (!MAP_SYSTEM(conn))
363 mask |= S_IXGRP;
364 if (!MAP_HIDDEN(conn))
365 mask |= S_IXOTH;
367 unixmode |= (st->st_mode & mask);
369 /* if we previously had any r bits set then leave them alone */
370 if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
371 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
372 unixmode |= tmp;
375 /* if we previously had any w bits set then leave them alone
376 whilst adding in the new w bits, if the new mode is not rdonly */
377 if (!IS_DOS_READONLY(dosmode)) {
378 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
381 if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0)
382 return 0;
384 if((errno != EPERM) && (errno != EACCES))
385 return -1;
387 if(!lp_dos_filemode(SNUM(conn)))
388 return -1;
390 /* We want DOS semantics, ie allow non owner with write permission to change the
391 bits on a file. Just like file_utime below.
394 /* Check if we have write access. */
395 if (CAN_WRITE(conn)) {
397 * We need to open the file with write access whilst
398 * still in our current user context. This ensures we
399 * are not violating security in doing the fchmod.
400 * This file open does *not* break any oplocks we are
401 * holding. We need to review this.... may need to
402 * break batch oplocks open by others. JRA.
404 files_struct *fsp = open_file_fchmod(conn,fname,st);
405 if (!fsp)
406 return -1;
407 become_root();
408 ret = SMB_VFS_FCHMOD(fsp, fsp->fd, unixmode);
409 unbecome_root();
410 close_file_fchmod(fsp);
413 return( ret );
416 /*******************************************************************
417 Wrapper around dos_utime that possibly allows DOS semantics rather
418 than POSIX.
419 *******************************************************************/
421 int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
423 extern struct current_user current_user;
424 SMB_STRUCT_STAT sb;
425 int ret = -1;
427 errno = 0;
429 if(SMB_VFS_UTIME(conn,fname, times) == 0)
430 return 0;
432 if((errno != EPERM) && (errno != EACCES))
433 return -1;
435 if(!lp_dos_filetimes(SNUM(conn)))
436 return -1;
438 /* We have permission (given by the Samba admin) to
439 break POSIX semantics and allow a user to change
440 the time on a file they don't own but can write to
441 (as DOS does).
444 if(SMB_VFS_STAT(conn,fname,&sb) != 0)
445 return -1;
447 /* Check if we have write access. */
448 if (CAN_WRITE(conn)) {
449 if (((sb.st_mode & S_IWOTH) || conn->admin_user ||
450 ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) ||
451 ((sb.st_mode & S_IWGRP) &&
452 in_group(sb.st_gid,current_user.gid,
453 current_user.ngroups,current_user.groups)))) {
454 /* We are allowed to become root and change the filetime. */
455 become_root();
456 ret = SMB_VFS_UTIME(conn,fname, times);
457 unbecome_root();
461 return ret;
464 /*******************************************************************
465 Change a filetime - possibly allowing DOS semantics.
466 *******************************************************************/
468 BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime)
470 struct utimbuf times;
472 if (null_mtime(mtime))
473 return(True);
475 times.modtime = times.actime = mtime;
477 if (file_utime(conn, fname, &times)) {
478 DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
479 return False;
482 return(True);