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 24extern int DEBUGLEVEL; 25 26/**************************************************************************** 27 change a dos mode to a unix mode 28 base permission for files: 29 if inheriting 30 apply read/write bits from parent directory. 31 else 32 everybody gets read bit set 33 dos readonly is represented in unix by removing everyone's write bit 34 dos archive is represented in unix by the user's execute bit 35 dos system is represented in unix by the group's execute bit 36 dos hidden is represented in unix by the other's execute bit 37 if !inheriting { 38 Then apply create mask, 39 then add force bits. 40 } 41 base permission for directories: 42 dos directory is represented in unix by unix's dir bit and the exec bit 43 if !inheriting { 44 Then apply create mask, 45 then add force bits. 46 } 47****************************************************************************/ 48mode_t unix_mode(connection_struct *conn,int dosmode,const char *fname) 49{ 50 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH); 51 mode_t dir_mode = 0; /* Mode of the parent directory if inheriting. */ 52 53 if ( !IS_DOS_READONLY(dosmode) ) 54 result |= (S_IWUSR | S_IWGRP | S_IWOTH); 55 56 if (fname && lp_inherit_perms(SNUM(conn))) { 57 char *dname; 58 SMB_STRUCT_STAT sbuf; 59 60 dname = parent_dirname(fname); 61 DEBUG(2,("unix_mode(%s) inheriting from %s\n",fname,dname)); 62 if (dos_stat(dname,&sbuf) != 0) { 63 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",fname,dname,strerror(errno))); 64 return(0); /* *** shouldn't happen! *** */ 65 } 66 67 /* Save for later - but explicitly remove setuid bit for safety. */ 68 dir_mode = sbuf.st_mode & ~S_ISUID; 69 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode)); 70 /* Clear "result" */ 71 result = 0; 72 } 73 74 if (IS_DOS_DIR(dosmode)) { 75 /* We never make directories read only for the owner as under DOS a user 76 can always create a file in a read-only directory. */ 77 result |= (S_IFDIR | S_IWUSR); 78 79 if (dir_mode) { 80 /* Inherit mode of parent directory. */ 81 result |= dir_mode; 82 } else { 83 /* Provisionally add all 'x' bits */ 84 result |= (S_IXUSR | S_IXGRP | S_IXOTH); 85 86 /* Apply directory mask */ 87 result &= lp_dir_mask(SNUM(conn)); 88 /* Add in force bits */ 89 result |= lp_force_dir_mode(SNUM(conn)); 90 } 91 } else { 92 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode)) 93 result |= S_IXUSR; 94 95 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode)) 96 result |= S_IXGRP; 97 98 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode)) 99 result |= S_IXOTH; 100 101 if (dir_mode) { 102 /* Inherit 666 component of parent directory mode */ 103 result |= dir_mode 104 & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH); 105 } else { 106 /* Apply mode mask */ 107 result &= lp_create_mask(SNUM(conn)); 108 /* Add in force bits */ 109 result |= lp_force_create_mode(SNUM(conn)); 110 } 111 } 112 return(result); 113} 114 115 116/**************************************************************************** 117 change a unix mode to a dos mode 118****************************************************************************/ 119int dos_mode(connection_struct *conn,char *path,SMB_STRUCT_STAT *sbuf) 120{ 121 int result = 0; 122 123 DEBUG(8,("dos_mode: %s\n", path)); 124 125 if ((sbuf->st_mode & S_IWUSR) == 0) 126 result |= aRONLY; 127 128 if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0)) 129 result |= aARCH; 130 131 if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0)) 132 result |= aSYSTEM; 133 134 if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0)) 135 result |= aHIDDEN; 136 137 if (S_ISDIR(sbuf->st_mode)) 138 result = aDIR | (result & aRONLY); 139 140#ifdef S_ISLNK 141#if LINKS_READ_ONLY 142 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode)) 143 result |= aRONLY; 144#endif 145#endif 146 147 /* hide files with a name starting with a . */ 148 if (lp_hide_dot_files(SNUM(conn))) 149 { 150 char *p = strrchr(path,'/'); 151 if (p) 152 p++; 153 else 154 p = path; 155 156 if (p[0] == '.' && p[1] != '.' && p[1] != 0) 157 result |= aHIDDEN; 158 } 159 160 /* Optimization : Only call is_hidden_path if it's not already 161 hidden. */ 162 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) 163 { 164 result |= aHIDDEN; 165 } 166 167 DEBUG(8,("dos_mode returning ")); 168 169 if (result & aHIDDEN) DEBUG(8, ("h")); 170 if (result & aRONLY ) DEBUG(8, ("r")); 171 if (result & aSYSTEM) DEBUG(8, ("s")); 172 if (result & aDIR ) DEBUG(8, ("d")); 173 if (result & aARCH ) DEBUG(8, ("a")); 174 175 DEBUG(8,("\n")); 176 177 return(result); 178} 179 180/******************************************************************* 181chmod a file - but preserve some bits 182********************************************************************/ 183int file_chmod(connection_struct *conn,char *fname,int dosmode,SMB_STRUCT_STAT *st) 184{ 185 SMB_STRUCT_STAT st1; 186 int mask=0; 187 mode_t tmp; 188 mode_t unixmode; 189 190 if (!st) { 191 st = &st1; 192 if (dos_stat(fname,st)) return(-1); 193 } 194 195 if (S_ISDIR(st->st_mode)) dosmode |= aDIR; 196 197 if (dos_mode(conn,fname,st) == dosmode) return(0); 198 199 unixmode = unix_mode(conn,dosmode,fname); 200 201 /* preserve the s bits */ 202 mask |= (S_ISUID | S_ISGID); 203 204 /* preserve the t bit */ 205#ifdef S_ISVTX 206 mask |= S_ISVTX; 207#endif 208 209 /* possibly preserve the x bits */ 210 if (!MAP_ARCHIVE(conn)) mask |= S_IXUSR; 211 if (!MAP_SYSTEM(conn)) mask |= S_IXGRP; 212 if (!MAP_HIDDEN(conn)) mask |= S_IXOTH; 213 214 unixmode |= (st->st_mode & mask); 215 216 /* if we previously had any r bits set then leave them alone */ 217 if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) { 218 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH); 219 unixmode |= tmp; 220 } 221 222 /* if we previously had any w bits set then leave them alone 223 whilst adding in the new w bits, if the new mode is not rdonly */ 224 if (!IS_DOS_READONLY(dosmode)) { 225 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)); 226 } 227 228 return(dos_chmod(fname,unixmode)); 229} 230 231 232/******************************************************************* 233Wrapper around dos_utime that possibly allows DOS semantics rather 234than POSIX. 235*******************************************************************/ 236int file_utime(connection_struct *conn, char *fname, struct utimbuf *times) 237{ 238 extern struct current_user current_user; 239 SMB_STRUCT_STAT sb; 240 int ret = -1; 241 242 errno = 0; 243 244 if(dos_utime(fname, times) == 0) 245 return 0; 246 247 if((errno != EPERM) && (errno != EACCES)) 248 return -1; 249 250 if(!lp_dos_filetimes(SNUM(conn))) 251 return -1; 252 253 /* We have permission (given by the Samba admin) to 254 break POSIX semantics and allow a user to change 255 the time on a file they don't own but can write to 256 (as DOS does). 257 */ 258 259 if(dos_stat(fname,&sb) != 0) 260 return -1; 261 262 /* Check if we have write access. */ 263 if (CAN_WRITE(conn)) { 264 if (((sb.st_mode & S_IWOTH) || 265 conn->admin_user || 266 ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) || 267 ((sb.st_mode & S_IWGRP) && 268 in_group(sb.st_gid,current_user.gid, 269 current_user.ngroups,current_user.groups)))) { 270 /* We are allowed to become root and change the filetime. */ 271 become_root(False); 272 ret = dos_utime(fname, times); 273 unbecome_root(False); 274 } 275 } 276 277 return ret; 278} 279 280/******************************************************************* 281Change a filetime - possibly allowing DOS semantics. 282*******************************************************************/ 283BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime) 284{ 285 struct utimbuf times; 286 287 if (null_mtime(mtime)) return(True); 288 289 times.modtime = times.actime = mtime; 290 291 if (file_utime(conn, fname, ×)) { 292 DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno))); 293 return False; 294 } 295 296 return(True); 297} 298