source: branches/samba-3.0/source/smbd/dosmode.c@ 157

Last change on this file since 157 was 124, checked in by Paul Smedley, 17 years ago

Update source to 3.0.28a

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