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

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

Use os2_isattribute to check for readonly, system, hidden, archive attributes

File size: 17.8 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 if (!st || (st && !VALID_STAT(*st))) {
456 st = &st1;
457 if (SMB_VFS_STAT(conn,fname,st))
458 return(-1);
459 }
460
461 get_acl_group_bits(conn, fname, &st->st_mode);
462
463 if (S_ISDIR(st->st_mode))
464 dosmode |= aDIR;
465 else
466 dosmode &= ~aDIR;
467
468 if (dos_mode(conn,fname,st) == dosmode)
469 return(0);
470
471 /* Store the DOS attributes in an EA by preference. */
472 if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
473 return 0;
474 }
475
476 unixmode = unix_mode(conn,dosmode,fname, parent_dir);
477
478 /* preserve the s bits */
479 mask |= (S_ISUID | S_ISGID);
480
481 /* preserve the t bit */
482#ifdef S_ISVTX
483 mask |= S_ISVTX;
484#endif
485
486 /* possibly preserve the x bits */
487 if (!MAP_ARCHIVE(conn))
488 mask |= S_IXUSR;
489 if (!MAP_SYSTEM(conn))
490 mask |= S_IXGRP;
491 if (!MAP_HIDDEN(conn))
492 mask |= S_IXOTH;
493
494 unixmode |= (st->st_mode & mask);
495
496 /* if we previously had any r bits set then leave them alone */
497 if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
498 unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
499 unixmode |= tmp;
500 }
501
502 /* if we previously had any w bits set then leave them alone
503 whilst adding in the new w bits, if the new mode is not rdonly */
504 if (!IS_DOS_READONLY(dosmode)) {
505 unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
506 }
507
508 if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0) {
509 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
510 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
511 return 0;
512 }
513
514 if((errno != EPERM) && (errno != EACCES))
515 return -1;
516
517 if(!lp_dos_filemode(SNUM(conn)))
518 return -1;
519
520 /* We want DOS semantics, ie allow non owner with write permission to change the
521 bits on a file. Just like file_ntimes below.
522 */
523
524 /* Check if we have write access. */
525 if (CAN_WRITE(conn)) {
526 /*
527 * We need to open the file with write access whilst
528 * still in our current user context. This ensures we
529 * are not violating security in doing the fchmod.
530 * This file open does *not* break any oplocks we are
531 * holding. We need to review this.... may need to
532 * break batch oplocks open by others. JRA.
533 */
534 files_struct *fsp;
535 if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp)))
536 return -1;
537 become_root();
538 ret = SMB_VFS_FCHMOD(fsp, fsp->fh->fd, unixmode);
539 unbecome_root();
540 close_file_fchmod(fsp);
541 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
542 FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
543 }
544
545 return( ret );
546}
547
548/*******************************************************************
549 Wrapper around the VFS ntimes that possibly allows DOS semantics rather
550 than POSIX.
551*******************************************************************/
552
553int file_ntimes(connection_struct *conn, const char *fname, const struct timespec ts[2])
554{
555 SMB_STRUCT_STAT sbuf;
556 int ret = -1;
557
558 errno = 0;
559 ZERO_STRUCT(sbuf);
560
561 /* Don't update the time on read-only shares */
562 /* We need this as set_filetime (which can be called on
563 close and other paths) can end up calling this function
564 without the NEED_WRITE protection. Found by :
565 Leo Weppelman <leo@wau.mis.ah.nl>
566 */
567
568 if (!CAN_WRITE(conn)) {
569 return 0;
570 }
571
572 if(SMB_VFS_NTIMES(conn, fname, ts) == 0) {
573 return 0;
574 }
575
576 if((errno != EPERM) && (errno != EACCES)) {
577 return -1;
578 }
579
580 if(!lp_dos_filetimes(SNUM(conn))) {
581 return -1;
582 }
583
584 /* We have permission (given by the Samba admin) to
585 break POSIX semantics and allow a user to change
586 the time on a file they don't own but can write to
587 (as DOS does).
588 */
589
590 /* Check if we have write access. */
591 if (can_write_to_file(conn, fname, &sbuf)) {
592 /* We are allowed to become root and change the filetime. */
593 become_root();
594 ret = SMB_VFS_NTIMES(conn, fname, ts);
595 unbecome_root();
596 }
597
598 return ret;
599}
600
601/*******************************************************************
602 Change a filetime - possibly allowing DOS semantics.
603*******************************************************************/
604
605BOOL set_filetime(connection_struct *conn, const char *fname,
606 const struct timespec mtime)
607{
608 struct timespec ts[2];
609
610 if (null_timespec(mtime)) {
611 return(True);
612 }
613
614 ts[1] = mtime; /* mtime. */
615 ts[0] = ts[1]; /* atime. */
616
617 if (file_ntimes(conn, fname, ts)) {
618 DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
619 return False;
620 }
621
622 notify_fname(conn, NOTIFY_ACTION_MODIFIED,
623 FILE_NOTIFY_CHANGE_LAST_WRITE, fname);
624
625 return True;
626}
Note: See TracBrowser for help on using the repository browser.