r3684: syncing up some changes for 3.0.9
[Samba.git] / source / smbd / dosmode.c
blob7199b3ebbf3e6d69daa8d7fd81d461631502def8
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(STAT_ST_BLOCKSIZE)
139 if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
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) ));
185 set_store_dos_attributes(SNUM(conn), False);
187 #endif
188 return False;
190 /* Null terminate string. */
191 attrstr[sizeret] = 0;
192 DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
194 if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
195 sscanf(attrstr, "%x", &dosattr) != 1) {
196 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
197 return False;
200 if (S_ISDIR(sbuf->st_mode)) {
201 dosattr |= aDIR;
203 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
205 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
207 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
208 if (dosattr & aRONLY ) DEBUG(8, ("r"));
209 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
210 if (dosattr & aDIR ) DEBUG(8, ("d"));
211 if (dosattr & aARCH ) DEBUG(8, ("a"));
213 DEBUG(8,("\n"));
215 return True;
218 /****************************************************************************
219 Set DOS attributes in an EA.
220 ****************************************************************************/
222 static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
224 fstring attrstr;
225 files_struct *fsp = NULL;
226 BOOL ret = False;
228 if (!lp_store_dos_attributes(SNUM(conn))) {
229 return False;
232 snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
233 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
234 if((errno != EPERM) && (errno != EACCES)) {
235 if (errno == ENOSYS
236 #if defined(ENOTSUP)
237 || errno == ENOTSUP) {
238 #else
240 #endif
241 set_store_dos_attributes(SNUM(conn), False);
243 return False;
246 /* We want DOS semantics, ie allow non owner with write permission to change the
247 bits on a file. Just like file_utime below.
250 /* Check if we have write access. */
251 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
252 return False;
255 * We need to open the file with write access whilst
256 * still in our current user context. This ensures we
257 * are not violating security in doing the setxattr.
260 fsp = open_file_fchmod(conn,path,sbuf);
261 if (!fsp)
262 return ret;
263 become_root();
264 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
265 ret = True;
267 unbecome_root();
268 close_file_fchmod(fsp);
269 return ret;
271 DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
272 return True;
275 /****************************************************************************
276 Change a unix mode to a dos mode.
277 ****************************************************************************/
279 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
281 uint32 result = 0;
283 DEBUG(8,("dos_mode: %s\n", path));
285 if (!VALID_STAT(*sbuf)) {
286 return 0;
289 /* Get the DOS attributes from an EA by preference. */
290 if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
291 return result;
294 result = dos_mode_from_sbuf(conn, sbuf);
296 /* Now do any modifications that depend on the path name. */
297 /* hide files with a name starting with a . */
298 if (lp_hide_dot_files(SNUM(conn))) {
299 const char *p = strrchr_m(path,'/');
300 if (p)
301 p++;
302 else
303 p = path;
305 if (p[0] == '.' && p[1] != '.' && p[1] != 0)
306 result |= aHIDDEN;
309 /* Optimization : Only call is_hidden_path if it's not already
310 hidden. */
311 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
312 result |= aHIDDEN;
315 DEBUG(8,("dos_mode returning "));
317 if (result & aHIDDEN) DEBUG(8, ("h"));
318 if (result & aRONLY ) DEBUG(8, ("r"));
319 if (result & aSYSTEM) DEBUG(8, ("s"));
320 if (result & aDIR ) DEBUG(8, ("d"));
321 if (result & aARCH ) DEBUG(8, ("a"));
323 DEBUG(8,("\n"));
325 return(result);
328 /*******************************************************************
329 chmod a file - but preserve some bits.
330 ********************************************************************/
332 int file_set_dosmode(connection_struct *conn, const char *fname, uint32 dosmode, SMB_STRUCT_STAT *st)
334 SMB_STRUCT_STAT st1;
335 int mask=0;
336 mode_t tmp;
337 mode_t unixmode;
338 int ret = -1;
340 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
341 if (!st) {
342 st = &st1;
343 if (SMB_VFS_STAT(conn,fname,st))
344 return(-1);
347 get_acl_group_bits(conn, fname, &st->st_mode);
349 if (S_ISDIR(st->st_mode))
350 dosmode |= aDIR;
351 else
352 dosmode &= ~aDIR;
354 if (dos_mode(conn,fname,st) == dosmode)
355 return(0);
357 /* Store the DOS attributes in an EA by preference. */
358 if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
359 return 0;
362 unixmode = unix_mode(conn,dosmode,fname);
364 /* preserve the s bits */
365 mask |= (S_ISUID | S_ISGID);
367 /* preserve the t bit */
368 #ifdef S_ISVTX
369 mask |= S_ISVTX;
370 #endif
372 /* possibly preserve the x bits */
373 if (!MAP_ARCHIVE(conn))
374 mask |= S_IXUSR;
375 if (!MAP_SYSTEM(conn))
376 mask |= S_IXGRP;
377 if (!MAP_HIDDEN(conn))
378 mask |= S_IXOTH;
380 unixmode |= (st->st_mode & mask);
382 /* if we previously had any r bits set then leave them alone */
383 if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
384 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
385 unixmode |= tmp;
388 /* if we previously had any w bits set then leave them alone
389 whilst adding in the new w bits, if the new mode is not rdonly */
390 if (!IS_DOS_READONLY(dosmode)) {
391 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
394 if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0)
395 return 0;
397 if((errno != EPERM) && (errno != EACCES))
398 return -1;
400 if(!lp_dos_filemode(SNUM(conn)))
401 return -1;
403 /* We want DOS semantics, ie allow non owner with write permission to change the
404 bits on a file. Just like file_utime below.
407 /* Check if we have write access. */
408 if (CAN_WRITE(conn)) {
410 * We need to open the file with write access whilst
411 * still in our current user context. This ensures we
412 * are not violating security in doing the fchmod.
413 * This file open does *not* break any oplocks we are
414 * holding. We need to review this.... may need to
415 * break batch oplocks open by others. JRA.
417 files_struct *fsp = open_file_fchmod(conn,fname,st);
418 if (!fsp)
419 return -1;
420 become_root();
421 ret = SMB_VFS_FCHMOD(fsp, fsp->fd, unixmode);
422 unbecome_root();
423 close_file_fchmod(fsp);
426 return( ret );
429 /*******************************************************************
430 Wrapper around dos_utime that possibly allows DOS semantics rather
431 than POSIX.
432 *******************************************************************/
434 int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
436 extern struct current_user current_user;
437 SMB_STRUCT_STAT sb;
438 int ret = -1;
440 errno = 0;
442 if(SMB_VFS_UTIME(conn,fname, times) == 0)
443 return 0;
445 if((errno != EPERM) && (errno != EACCES))
446 return -1;
448 if(!lp_dos_filetimes(SNUM(conn)))
449 return -1;
451 /* We have permission (given by the Samba admin) to
452 break POSIX semantics and allow a user to change
453 the time on a file they don't own but can write to
454 (as DOS does).
457 if(SMB_VFS_STAT(conn,fname,&sb) != 0)
458 return -1;
460 /* Check if we have write access. */
461 if (CAN_WRITE(conn)) {
462 if (((sb.st_mode & S_IWOTH) || conn->admin_user ||
463 ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) ||
464 ((sb.st_mode & S_IWGRP) &&
465 in_group(sb.st_gid,current_user.gid,
466 current_user.ngroups,current_user.groups)))) {
467 /* We are allowed to become root and change the filetime. */
468 become_root();
469 ret = SMB_VFS_UTIME(conn,fname, times);
470 unbecome_root();
474 return ret;
477 /*******************************************************************
478 Change a filetime - possibly allowing DOS semantics.
479 *******************************************************************/
481 BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime)
483 struct utimbuf times;
485 if (null_mtime(mtime))
486 return(True);
488 times.modtime = times.actime = mtime;
490 if (file_utime(conn, fname, &times)) {
491 DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
492 return False;
495 return(True);