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

Last change on this file since 165 was 165, checked in by Paul Smedley, 16 years ago

Add 'missing' 3.0.34 diffs

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