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

Last change on this file since 223 was 173, checked in by Herwig Bauernfeind, 16 years ago

Fix for Ticket #41

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