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