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