1/* 2 Unix SMB/CIFS implementation. 3 dos mode handling functions 4 Copyright (C) Andrew Tridgell 1992-1998 5 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. 10 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. 15 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. 19*/ 20 21#include "includes.h" 22 23/**************************************************************************** 24 Change a dos mode to a unix mode. 25 Base permission for files: 26 if creating file and 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. 37 } 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. 43 } 44****************************************************************************/ 45 46mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname, BOOL creating_file) 47{ 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. */ 50 51 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) { 52 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH); 53 } 54 55 if (fname && creating_file && lp_inherit_perms(SNUM(conn))) { 56 char *dname; 57 SMB_STRUCT_STAT sbuf; 58 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! *** */ 64 } 65 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; 71 } 72 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); 77 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); 84 85 /* Apply directory mask */ 86 result &= lp_dir_mask(SNUM(conn)); 87 /* Add in force bits */ 88 result |= lp_force_dir_mode(SNUM(conn)); 89 } 90 } else { 91 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode)) 92 result |= S_IXUSR; 93 94 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode)) 95 result |= S_IXGRP; 96 97 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode)) 98 result |= S_IXOTH; 99 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)); 108 } 109 } 110 111 DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result )); 112 return(result); 113} 114 115/**************************************************************************** 116 Change a unix mode to a dos mode. 117****************************************************************************/ 118 119uint32 dos_mode_from_sbuf(connection_struct *conn, SMB_STRUCT_STAT *sbuf) 120{ 121 int result = 0; 122 123 if ((sbuf->st_mode & S_IWUSR) == 0) 124 result |= aRONLY; 125 126 if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0)) 127 result |= aARCH; 128 129 if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0)) 130 result |= aSYSTEM; 131 132 if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0)) 133 result |= aHIDDEN; 134 135 if (S_ISDIR(sbuf->st_mode)) 136 result = aDIR | (result & aRONLY); 137 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; 141 } 142#endif 143 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 150 151 DEBUG(8,("dos_mode_from_sbuf returning ")); 152 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")); 158 159 DEBUG(8,("\n")); 160 return result; 161} 162 163/**************************************************************************** 164 Get DOS attributes from an EA. 165****************************************************************************/ 166 167static BOOL get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr) 168{ 169 ssize_t sizeret; 170 fstring attrstr; 171 unsigned int dosattr; 172 173 if (!lp_store_dos_attributes(SNUM(conn))) { 174 return False; 175 } 176 177 *pattr = 0; 178 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); 186 } 187#endif 188 return False; 189 } 190 /* Null terminate string. */ 191 attrstr[sizeret] = 0; 192 DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr)); 193 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; 198 } 199 200 if (S_ISDIR(sbuf->st_mode)) { 201 dosattr |= aDIR; 202 } 203 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK); 204 205 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr)); 206 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")); 212 213 DEBUG(8,("\n")); 214 215 return True; 216} 217 218/**************************************************************************** 219 Set DOS attributes in an EA. 220****************************************************************************/ 221 222static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode) 223{ 224 fstring attrstr; 225 files_struct *fsp = NULL; 226 BOOL ret = False; 227 228 if (!lp_store_dos_attributes(SNUM(conn))) { 229 return False; 230 } 231 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 239 ) { 240#endif 241 set_store_dos_attributes(SNUM(conn), False); 242 } 243 return False; 244 } 245 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. 248 */ 249 250 /* Check if we have write access. */ 251 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn))) 252 return False; 253 254 /* 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. 258 */ 259 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; 266 } 267 unbecome_root(); 268 close_file_fchmod(fsp); 269 return ret; 270 } 271 DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path)); 272 return True; 273} 274 275/**************************************************************************** 276 Change a unix mode to a dos mode. 277****************************************************************************/ 278 279uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf) 280{ 281 uint32 result = 0; 282 283 DEBUG(8,("dos_mode: %s\n", path)); 284 285 if (!VALID_STAT(*sbuf)) { 286 return 0; 287 } 288 289 /* Get the DOS attributes from an EA by preference. */ 290 if (get_ea_dos_attribute(conn, path, sbuf, &result)) { 291 return result; 292 } 293 294 result = dos_mode_from_sbuf(conn, sbuf); 295 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; 304 305 if (p[0] == '.' && p[1] != '.' && p[1] != 0) 306 result |= aHIDDEN; 307 } 308 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; 313 } 314 315 DEBUG(8,("dos_mode returning ")); 316 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")); 322 323 DEBUG(8,("\n")); 324 325 return(result); 326} 327 328/******************************************************************* 329 chmod a file - but preserve some bits. 330********************************************************************/ 331 332int file_set_dosmode(connection_struct *conn, const char *fname, uint32 dosmode, SMB_STRUCT_STAT *st, BOOL creating_file) 333{ 334 SMB_STRUCT_STAT st1; 335 int mask=0; 336 mode_t tmp; 337 mode_t unixmode; 338 int ret = -1; 339 340 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname)); 341 if (!st || (st && !VALID_STAT(*st))) { 342 st = &st1; 343 if (SMB_VFS_STAT(conn,fname,st)) 344 return(-1); 345 } 346 347 get_acl_group_bits(conn, fname, &st->st_mode); 348 349 if (S_ISDIR(st->st_mode)) 350 dosmode |= aDIR; 351 else 352 dosmode &= ~aDIR; 353 354 if (dos_mode(conn,fname,st) == dosmode) 355 return(0); 356 357 /* Store the DOS attributes in an EA by preference. */ 358 if (set_ea_dos_attribute(conn, fname, st, dosmode)) { 359 return 0; 360 } 361 362 unixmode = unix_mode(conn,dosmode,fname, creating_file); 363 364 /* preserve the s bits */ 365 mask |= (S_ISUID | S_ISGID); 366 367 /* preserve the t bit */ 368#ifdef S_ISVTX 369 mask |= S_ISVTX; 370#endif 371 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; 379 380 unixmode |= (st->st_mode & mask); 381 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; 386 } 387 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)); 392 } 393 394 if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0) 395 return 0; 396 397 if((errno != EPERM) && (errno != EACCES)) 398 return -1; 399 400 if(!lp_dos_filemode(SNUM(conn))) 401 return -1; 402 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. 405 */ 406 407 /* Check if we have write access. */ 408 if (CAN_WRITE(conn)) { 409 /* 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. 416 */ 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); 424 } 425 426 return( ret ); 427} 428 429/******************************************************************* 430 Wrapper around dos_utime that possibly allows DOS semantics rather 431 than POSIX. 432*******************************************************************/ 433 434int file_utime(connection_struct *conn, char *fname, struct utimbuf *times) 435{ 436 extern struct current_user current_user; 437 SMB_STRUCT_STAT sb; 438 int ret = -1; 439 440 errno = 0; 441 442 if(SMB_VFS_UTIME(conn,fname, times) == 0) 443 return 0; 444 445 if((errno != EPERM) && (errno != EACCES)) 446 return -1; 447 448 if(!lp_dos_filetimes(SNUM(conn))) 449 return -1; 450 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). 455 */ 456 457 if(SMB_VFS_STAT(conn,fname,&sb) != 0) 458 return -1; 459 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(); 471 } 472 } 473 474 return ret; 475} 476 477/******************************************************************* 478 Change a filetime - possibly allowing DOS semantics. 479*******************************************************************/ 480 481BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime) 482{ 483 struct utimbuf times; 484 485 if (null_mtime(mtime)) 486 return(True); 487 488 times.modtime = times.actime = mtime; 489 490 if (file_utime(conn, fname, ×)) { 491 DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno))); 492 return False; 493 } 494 495 return(True); 496} 497