source: trunk/samba-3.0.25pre1/source/smbd/dosmode.c@ 5

Last change on this file since 5 was 1, checked in by Paul Smedley, 18 years ago

Initial code import

File size: 16.9 KB
Line 
1/*
2 Unix SMB/CIFS implementation.
3 dos mode handling functions
4 Copyright (C) Andrew Tridgell 1992-1998
5 Copyright (C) James Peach 2006
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
24static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
25{
26#if defined (HAVE_STAT_ST_BLOCKS) && defined(STAT_ST_BLOCKSIZE)
27 if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)STAT_ST_BLOCKSIZE) {
28 return FILE_ATTRIBUTE_SPARSE;
29 }
30#endif
31 return 0;
32}
33
34/****************************************************************************
35 Work out whether this file is offline
36****************************************************************************/
37
38static uint32 set_offline_flag(connection_struct *conn, const char *const path)
39{
40 if (ISDOT(path) || ISDOTDOT(path)) {
41 return 0;
42 }
43
44 if (!lp_dmapi_support(SNUM(conn)) || !dmapi_have_session()) {
45 return 0;
46 }
47
48 return dmapi_file_flags(path);
49}
50
51/****************************************************************************
52 Change a dos mode to a unix mode.
53 Base permission for files:
54 if creating file and inheriting (i.e. parent_dir != NULL)
55 apply read/write bits from parent directory.
56 else
57 everybody gets read bit set
58 dos readonly is represented in unix by removing everyone's write bit
59 dos archive is represented in unix by the user's execute bit
60 dos system is represented in unix by the group's execute bit
61 dos hidden is represented in unix by the other's execute bit
62 if !inheriting {
63 Then apply create mask,
64 then add force bits.
65 }
66 Base permission for directories:
67 dos directory is represented in unix by unix's dir bit and the exec bit
68 if !inheriting {
69 Then apply create mask,
70 then add force bits.
71 }
72****************************************************************************/
73
74mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname,
75 const char *inherit_from_dir)
76{
77 mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
78 mode_t dir_mode = 0; /* Mode of the inherit_from directory if
79 * inheriting. */
80
81 if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) {
82 result &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
83 }
84
85 if (fname && (inherit_from_dir != NULL)
86 && lp_inherit_perms(SNUM(conn))) {
87 SMB_STRUCT_STAT sbuf;
88
89 DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname,
90 inherit_from_dir));
91 if (SMB_VFS_STAT(conn, inherit_from_dir, &sbuf) != 0) {
92 DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname,
93 inherit_from_dir, strerror(errno)));
94 return(0); /* *** shouldn't happen! *** */
95 }
96
97 /* Save for later - but explicitly remove setuid bit for safety. */
98 dir_mode = sbuf.st_mode & ~S_ISUID;
99 DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
100 /* Clear "result" */
101 result = 0;
102 }
103
104 if (IS_DOS_DIR(dosmode)) {
105 /* We never make directories read only for the owner as under DOS a user
106 can always create a file in a read-only directory. */
107 result |= (S_IFDIR | S_IWUSR);
108
109 if (dir_mode) {
110 /* Inherit mode of parent directory. */
111 result |= dir_mode;
112 } else {
113 /* Provisionally add all 'x' bits */
114 result |= (S_IXUSR | S_IXGRP | S_IXOTH);
115
116 /* Apply directory mask */
117 result &= lp_dir_mask(SNUM(conn));
118 /* Add in force bits */
119 result |= lp_force_dir_mode(SNUM(conn));
120 }
121 } else {
122 if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
123 result |= S_IXUSR;
124
125 if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
126 result |= S_IXGRP;
127
128 if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
129 result |= S_IXOTH;
130
131 if (dir_mode) {
132 /* Inherit 666 component of parent directory mode */
133 result |= dir_mode & (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
134 } else {
135 /* Apply mode mask */
136 result &= lp_create_mask(SNUM(conn));
137 /* Add in force bits */
138 result |= lp_force_create_mode(SNUM(conn));
139 }
140 }
141
142 DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
143 return(result);
144}
145
146/****************************************************************************
147 Change a unix mode to a dos mode.
148****************************************************************************/
149
150static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf)
151{
152 int result = 0;
153 enum mapreadonly_options ro_opts = (enum mapreadonly_options)lp_map_readonly(SNUM(conn));
154
155 if (ro_opts == MAP_READONLY_YES) {
156 /* Original Samba method - map inverse of user "w" bit. */
157 if ((sbuf->st_mode & S_IWUSR) == 0) {
158 result |= aRONLY;
159 }
160 } else if (ro_opts == MAP_READONLY_PERMISSIONS) {
161 /* Check actual permissions for read-only. */
162 if (!can_write_to_file(conn, path, sbuf)) {
163 result |= aRONLY;
164 }
165 } /* Else never set the readonly bit. */
166
167 if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
168 result |= aARCH;
169
170 if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
171 result |= aSYSTEM;
172
173 if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
174 result |= aHIDDEN;
175
176 if (S_ISDIR(sbuf->st_mode))
177 result = aDIR | (result & aRONLY);
178
179 result |= set_sparse_flag(sbuf);
180
181#ifdef S_ISLNK
182#if LINKS_READ_ONLY
183 if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
184 result |= aRONLY;
185#endif
186#endif
187
188 DEBUG(8,("dos_mode_from_sbuf returning "));
189
190 if (result & aHIDDEN) DEBUG(8, ("h"));
191 if (result & aRONLY ) DEBUG(8, ("r"));
192 if (result & aSYSTEM) DEBUG(8, ("s"));
193 if (result & aDIR ) DEBUG(8, ("d"));
194 if (result & aARCH ) DEBUG(8, ("a"));
195
196 DEBUG(8,("\n"));
197 return result;
198}
199
200/****************************************************************************
201 Get DOS attributes from an EA.
202****************************************************************************/
203
204static BOOL get_ea_dos_attribute(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf, uint32 *pattr)
205{
206 ssize_t sizeret;
207 fstring attrstr;
208 unsigned int dosattr;
209
210 if (!lp_store_dos_attributes(SNUM(conn))) {
211 return False;
212 }
213
214 /* Don't reset pattr to zero as we may already have filename-based attributes we
215 need to preserve. */
216
217 sizeret = SMB_VFS_GETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr));
218 if (sizeret == -1) {
219#if defined(ENOTSUP) && defined(ENOATTR)
220 if ((errno != ENOTSUP) && (errno != ENOATTR) && (errno != EACCES) && (errno != EPERM)) {
221 DEBUG(1,("get_ea_dos_attributes: Cannot get attribute from EA on file %s: Error = %s\n",
222 path, strerror(errno) ));
223 set_store_dos_attributes(SNUM(conn), False);
224 }
225#endif
226 return False;
227 }
228 /* Null terminate string. */
229 attrstr[sizeret] = 0;
230 DEBUG(10,("get_ea_dos_attribute: %s attrstr = %s\n", path, attrstr));
231
232 if (sizeret < 2 || attrstr[0] != '0' || attrstr[1] != 'x' ||
233 sscanf(attrstr, "%x", &dosattr) != 1) {
234 DEBUG(1,("get_ea_dos_attributes: Badly formed DOSATTRIB on file %s - %s\n", path, attrstr));
235 return False;
236 }
237
238 if (S_ISDIR(sbuf->st_mode)) {
239 dosattr |= aDIR;
240 }
241 *pattr = (uint32)(dosattr & SAMBA_ATTRIBUTES_MASK);
242
243 DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr));
244
245 if (dosattr & aHIDDEN) DEBUG(8, ("h"));
246 if (dosattr & aRONLY ) DEBUG(8, ("r"));
247 if (dosattr & aSYSTEM) DEBUG(8, ("s"));
248 if (dosattr & aDIR ) DEBUG(8, ("d"));
249 if (dosattr & aARCH ) DEBUG(8, ("a"));
250
251 DEBUG(8,("\n"));
252
253 return True;
254}
255
256/****************************************************************************
257 Set DOS attributes in an EA.
258****************************************************************************/
259
260static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode)
261{
262 fstring attrstr;
263 files_struct *fsp = NULL;
264 BOOL ret = False;
265
266 if (!lp_store_dos_attributes(SNUM(conn))) {
267 return False;
268 }
269
270 snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK);
271 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) {
272 if((errno != EPERM) && (errno != EACCES)) {
273 if (errno == ENOSYS
274#if defined(ENOTSUP)
275 || errno == ENOTSUP) {
276#else
277 ) {
278#endif
279 set_store_dos_attributes(SNUM(conn), False);
280 }
281 return False;
282 }
283
284 /* We want DOS semantics, ie allow non owner with write permission to change the
285 bits on a file. Just like file_utime below.
286 */
287
288 /* Check if we have write access. */
289 if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn)))
290 return False;
291
292 /*
293 * We need to open the file with write access whilst
294 * still in our current user context. This ensures we
295 * are not violating security in doing the setxattr.
296 */
297
298 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,path,sbuf,&fsp)))
299 return ret;
300 become_root();
301 if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) {
302 ret = True;
303 }
304 unbecome_root();
305 close_file_fchmod(fsp);
306 return ret;
307 }
308 DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path));
309 return True;
310}
311
312/****************************************************************************
313 Change a unix mode to a dos mode for an ms dfs link.
314****************************************************************************/
315
316uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
317{
318 uint32 result = 0;
319
320 DEBUG(8,("dos_mode_msdfs: %s\n", path));
321
322 if (!VALID_STAT(*sbuf)) {
323 return 0;
324 }
325
326 /* First do any modifications that depend on the path name. */
327 /* hide files with a name starting with a . */
328 if (lp_hide_dot_files(SNUM(conn))) {
329 const char *p = strrchr_m(path,'/');
330 if (p) {
331 p++;
332 } else {
333 p = path;
334 }
335
336 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
337 result |= aHIDDEN;
338 }
339 }
340
341 result |= dos_mode_from_sbuf(conn, path, sbuf);
342
343 /* Optimization : Only call is_hidden_path if it's not already
344 hidden. */
345 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
346 result |= aHIDDEN;
347 }
348
349 DEBUG(8,("dos_mode_msdfs returning "));
350
351 if (result & aHIDDEN) DEBUG(8, ("h"));
352 if (result & aRONLY ) DEBUG(8, ("r"));
353 if (result & aSYSTEM) DEBUG(8, ("s"));
354 if (result & aDIR ) DEBUG(8, ("d"));
355 if (result & aARCH ) DEBUG(8, ("a"));
356 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
357
358 DEBUG(8,("\n"));
359
360 return(result);
361}
362
363/****************************************************************************
364 Change a unix mode to a dos mode.
365****************************************************************************/
366
367uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
368{
369 uint32 result = 0;
370
371 DEBUG(8,("dos_mode: %s\n", path));
372
373 if (!VALID_STAT(*sbuf)) {
374 return 0;
375 }
376
377 /* First do any modifications that depend on the path name. */
378 /* hide files with a name starting with a . */
379 if (lp_hide_dot_files(SNUM(conn))) {
380 const char *p = strrchr_m(path,'/');
381 if (p) {
382 p++;
383 } else {
384 p = path;
385 }
386
387 if (p[0] == '.' && p[1] != '.' && p[1] != 0) {
388 result |= aHIDDEN;
389 }
390 }
391
392 /* Get the DOS attributes from an EA by preference. */
393 if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
394 result |= set_sparse_flag(sbuf);
395 } else {
396 result |= dos_mode_from_sbuf(conn, path, sbuf);
397 }
398
399 if (S_ISREG(sbuf->st_mode)) {
400 result |= set_offline_flag(conn, path);
401 }
402
403 /* Optimization : Only call is_hidden_path if it's not already
404 hidden. */
405 if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
406 result |= aHIDDEN;
407 }
408
409 DEBUG(8,("dos_mode returning "));
410
411 if (result & aHIDDEN) DEBUG(8, ("h"));
412 if (result & aRONLY ) DEBUG(8, ("r"));
413 if (result & aSYSTEM) DEBUG(8, ("s"));
414 if (result & aDIR ) DEBUG(8, ("d"));
415 if (result & aARCH ) DEBUG(8, ("a"));
416 if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]"));
417
418 DEBUG(8,("\n"));
419
420 return(result);
421}
422
423/*******************************************************************
424 chmod a file - but preserve some bits.
425********************************************************************/
426
427int file_set_dosmode(connection_struct *conn, const char *fname,
428 uint32 dosmode, SMB_STRUCT_STAT *st,
429 const char *parent_dir)
430{
431 SMB_STRUCT_STAT st1;
432 int mask=0;
433 mode_t tmp;
434 mode_t unixmode;
435 int ret = -1;
436
437 /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */
438 dosmode &= SAMBA_ATTRIBUTES_MASK;
439
440 DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname));
441 if (!st || (st && !VALID_STAT(*st))) {
442 st = &st1;
443 if (SMB_VFS_STAT(conn,fname,st))
444 return(-1);
445 }
446
447 get_acl_group_bits(conn, fname, &st->st_mode);
448
449 if (S_ISDIR(st->st_mode))
450 dosmode |= aDIR;
451 else
452 dosmode &= ~aDIR;
453
454 if (dos_mode(conn,fname,st) == dosmode)
455 return(0);
456
457 /* Store the DOS attributes in an EA by preference. */
458 if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
459 return 0;
460 }
461
462 unixmode = unix_mode(conn,dosmode,fname, parent_dir);
463
464 /* preserve the s bits */
465 mask |= (S_ISUID | S_ISGID);
466
467 /* preserve the t bit */
468#ifdef S_ISVTX
469 mask |= S_ISVTX;
470#endif
471
472 /* possibly preserve the x bits */
473 if (!MAP_ARCHIVE(conn))
474 mask |= S_IXUSR;
475 if (!MAP_SYSTEM(conn))
476 mask |= S_IXGRP;
477 if (!MAP_HIDDEN(conn))
478 mask |= S_IXOTH;
479
480 unixmode |= (st->st_mode & mask);
481
482 /* if we previously had any r bits set then leave them alone */
483 if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
484 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
485 unixmode |= tmp;
486 }
487
488 /* if we previously had any w bits set then leave them alone
489 whilst adding in the new w bits, if the new mode is not rdonly */
490 if (!IS_DOS_READONLY(dosmode)) {
491 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
492 }
493
494 if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0) {
495 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
496 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
497 return 0;
498 }
499
500 if((errno != EPERM) && (errno != EACCES))
501 return -1;
502
503 if(!lp_dos_filemode(SNUM(conn)))
504 return -1;
505
506 /* We want DOS semantics, ie allow non owner with write permission to change the
507 bits on a file. Just like file_utime below.
508 */
509
510 /* Check if we have write access. */
511 if (CAN_WRITE(conn)) {
512 /*
513 * We need to open the file with write access whilst
514 * still in our current user context. This ensures we
515 * are not violating security in doing the fchmod.
516 * This file open does *not* break any oplocks we are
517 * holding. We need to review this.... may need to
518 * break batch oplocks open by others. JRA.
519 */
520 files_struct *fsp;
521 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
522 return -1;
523 become_root();
524 ret = SMB_VFS_FCHMOD(fsp, fsp->fh->fd, unixmode);
525 unbecome_root();
526 close_file_fchmod(fsp);
527 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
528 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
529 }
530
531 return( ret );
532}
533
534/*******************************************************************
535 Wrapper around dos_utime that possibly allows DOS semantics rather
536 than POSIX.
537*******************************************************************/
538
539int file_utime(connection_struct *conn, const char *fname, struct utimbuf *times)
540{
541 SMB_STRUCT_STAT sbuf;
542 int ret = -1;
543
544 errno = 0;
545 ZERO_STRUCT(sbuf);
546
547 /* Don't update the time on read-only shares */
548 /* We need this as set_filetime (which can be called on
549 close and other paths) can end up calling this function
550 without the NEED_WRITE protection. Found by :
551 Leo Weppelman <leo@wau.mis.ah.nl>
552 */
553
554 if (!CAN_WRITE(conn)) {
555 return 0;
556 }
557
558 if(SMB_VFS_UTIME(conn,fname, times) == 0)
559 return 0;
560
561 if((errno != EPERM) && (errno != EACCES))
562 return -1;
563
564 if(!lp_dos_filetimes(SNUM(conn)))
565 return -1;
566
567 /* We have permission (given by the Samba admin) to
568 break POSIX semantics and allow a user to change
569 the time on a file they don't own but can write to
570 (as DOS does).
571 */
572
573 /* Check if we have write access. */
574 if (can_write_to_file(conn, fname, &sbuf)) {
575 /* We are allowed to become root and change the filetime. */
576 become_root();
577 ret = SMB_VFS_UTIME(conn,fname, times);
578 unbecome_root();
579 }
580
581 return ret;
582}
583
584/*******************************************************************
585 Change a filetime - possibly allowing DOS semantics.
586*******************************************************************/
587
588BOOL set_filetime(connection_struct *conn, const char *fname, time_t mtime)
589{
590 struct utimbuf times;
591
592 if (null_mtime(mtime))
593 return(True);
594
595 times.modtime = times.actime = mtime;
596
597 if (file_utime(conn, fname, &times)) {
598 DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
599 return False;
600 }
601
602 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
603 FILE_NOTIFY_CHANGE_LAST_WRITE, fname);
604
605 return(True);
606}
Note: See TracBrowser for help on using the repository browser.