1/*
2   Unix SMB/Netbios implementation.
3   Version 1.9.
4   dos mode handling functions
5   Copyright (C) Andrew Tridgell 1992-1998
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
24extern int DEBUGLEVEL;
25
26/****************************************************************************
27  change a dos mode to a unix mode
28    base permission for files:
29         if inheriting
30           apply read/write bits from parent directory.
31         else
32           everybody gets read bit set
33         dos readonly is represented in unix by removing everyone's write bit
34         dos archive is represented in unix by the user's execute bit
35         dos system is represented in unix by the group's execute bit
36         dos hidden is represented in unix by the other's execute bit
37         if !inheriting {
38           Then apply create mask,
39           then add force bits.
40         }
41    base permission for directories:
42         dos directory is represented in unix by unix's dir bit and the exec bit
43         if !inheriting {
44           Then apply create mask,
45           then add force bits.
46         }
47****************************************************************************/
48mode_t unix_mode(connection_struct *conn,int dosmode,const char *fname)
49{
50  mode_t result = (S_IRUSR | S_IRGRP | S_IROTH);
51  mode_t dir_mode = 0; /* Mode of the parent directory if inheriting. */
52
53  if ( !IS_DOS_READONLY(dosmode) )
54    result |= (S_IWUSR | S_IWGRP | S_IWOTH);
55
56  if (fname && lp_inherit_perms(SNUM(conn))) {
57    char *dname;
58    SMB_STRUCT_STAT sbuf;
59
60    dname = parent_dirname(fname);
61    DEBUG(2,("unix_mode(%s) inheriting from %s\n",fname,dname));
62    if (dos_stat(dname,&sbuf) != 0) {
63      DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",fname,dname,strerror(errno)));
64      return(0);      /* *** shouldn't happen! *** */
65    }
66
67    /* Save for later - but explicitly remove setuid bit for safety. */
68    dir_mode = sbuf.st_mode & ~S_ISUID;
69    DEBUG(2,("unix_mode(%s) inherit mode %o\n",fname,(int)dir_mode));
70    /* Clear "result" */
71    result = 0;
72  }
73
74  if (IS_DOS_DIR(dosmode)) {
75    /* We never make directories read only for the owner as under DOS a user
76       can always create a file in a read-only directory. */
77    result |= (S_IFDIR | S_IWUSR);
78
79    if (dir_mode) {
80      /* Inherit mode of parent directory. */
81      result |= dir_mode;
82    } else {
83      /* Provisionally add all 'x' bits */
84      result |= (S_IXUSR | S_IXGRP | S_IXOTH);
85
86      /* Apply directory mask */
87      result &= lp_dir_mask(SNUM(conn));
88      /* Add in force bits */
89      result |= lp_force_dir_mode(SNUM(conn));
90    }
91  } else {
92    if (lp_map_archive(SNUM(conn)) && IS_DOS_ARCHIVE(dosmode))
93      result |= S_IXUSR;
94
95    if (lp_map_system(SNUM(conn)) && IS_DOS_SYSTEM(dosmode))
96      result |= S_IXGRP;
97
98    if (lp_map_hidden(SNUM(conn)) && IS_DOS_HIDDEN(dosmode))
99      result |= S_IXOTH;
100
101    if (dir_mode) {
102      /* Inherit 666 component of parent directory mode */
103      result |= dir_mode
104        &  (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
105    } else {
106      /* Apply mode mask */
107      result &= lp_create_mask(SNUM(conn));
108      /* Add in force bits */
109      result |= lp_force_create_mode(SNUM(conn));
110    }
111  }
112  return(result);
113}
114
115
116/****************************************************************************
117  change a unix mode to a dos mode
118****************************************************************************/
119int dos_mode(connection_struct *conn,char *path,SMB_STRUCT_STAT *sbuf)
120{
121  int result = 0;
122
123  DEBUG(8,("dos_mode: %s\n", path));
124
125  if ((sbuf->st_mode & S_IWUSR) == 0)
126      result |= aRONLY;
127
128  if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
129    result |= aARCH;
130
131  if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
132    result |= aSYSTEM;
133
134  if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
135    result |= aHIDDEN;
136
137  if (S_ISDIR(sbuf->st_mode))
138    result = aDIR | (result & aRONLY);
139
140#ifdef S_ISLNK
141#if LINKS_READ_ONLY
142  if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
143    result |= aRONLY;
144#endif
145#endif
146
147  /* hide files with a name starting with a . */
148  if (lp_hide_dot_files(SNUM(conn)))
149    {
150      char *p = strrchr(path,'/');
151      if (p)
152	p++;
153      else
154	p = path;
155
156      if (p[0] == '.' && p[1] != '.' && p[1] != 0)
157	result |= aHIDDEN;
158    }
159
160  /* Optimization : Only call is_hidden_path if it's not already
161     hidden. */
162  if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path))
163  {
164    result |= aHIDDEN;
165  }
166
167  DEBUG(8,("dos_mode returning "));
168
169  if (result & aHIDDEN) DEBUG(8, ("h"));
170  if (result & aRONLY ) DEBUG(8, ("r"));
171  if (result & aSYSTEM) DEBUG(8, ("s"));
172  if (result & aDIR   ) DEBUG(8, ("d"));
173  if (result & aARCH  ) DEBUG(8, ("a"));
174
175  DEBUG(8,("\n"));
176
177  return(result);
178}
179
180/*******************************************************************
181chmod a file - but preserve some bits
182********************************************************************/
183int file_chmod(connection_struct *conn,char *fname,int dosmode,SMB_STRUCT_STAT *st)
184{
185  SMB_STRUCT_STAT st1;
186  int mask=0;
187  mode_t tmp;
188  mode_t unixmode;
189
190  if (!st) {
191    st = &st1;
192    if (dos_stat(fname,st)) return(-1);
193  }
194
195  if (S_ISDIR(st->st_mode)) dosmode |= aDIR;
196
197  if (dos_mode(conn,fname,st) == dosmode) return(0);
198
199  unixmode = unix_mode(conn,dosmode,fname);
200
201  /* preserve the s bits */
202  mask |= (S_ISUID | S_ISGID);
203
204  /* preserve the t bit */
205#ifdef S_ISVTX
206  mask |= S_ISVTX;
207#endif
208
209  /* possibly preserve the x bits */
210  if (!MAP_ARCHIVE(conn)) mask |= S_IXUSR;
211  if (!MAP_SYSTEM(conn)) mask |= S_IXGRP;
212  if (!MAP_HIDDEN(conn)) mask |= S_IXOTH;
213
214  unixmode |= (st->st_mode & mask);
215
216  /* if we previously had any r bits set then leave them alone */
217  if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
218    unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
219    unixmode |= tmp;
220  }
221
222  /* if we previously had any w bits set then leave them alone
223   whilst adding in the new w bits, if the new mode is not rdonly */
224  if (!IS_DOS_READONLY(dosmode)) {
225    unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
226  }
227
228  return(dos_chmod(fname,unixmode));
229}
230
231
232/*******************************************************************
233Wrapper around dos_utime that possibly allows DOS semantics rather
234than POSIX.
235*******************************************************************/
236int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
237{
238  extern struct current_user current_user;
239  SMB_STRUCT_STAT sb;
240  int ret = -1;
241
242  errno = 0;
243
244  if(dos_utime(fname, times) == 0)
245    return 0;
246
247  if((errno != EPERM) && (errno != EACCES))
248    return -1;
249
250  if(!lp_dos_filetimes(SNUM(conn)))
251    return -1;
252
253  /* We have permission (given by the Samba admin) to
254     break POSIX semantics and allow a user to change
255     the time on a file they don't own but can write to
256     (as DOS does).
257   */
258
259  if(dos_stat(fname,&sb) != 0)
260    return -1;
261
262  /* Check if we have write access. */
263  if (CAN_WRITE(conn)) {
264	  if (((sb.st_mode & S_IWOTH) ||
265	       conn->admin_user ||
266	       ((sb.st_mode & S_IWUSR) && current_user.uid==sb.st_uid) ||
267	       ((sb.st_mode & S_IWGRP) &&
268		in_group(sb.st_gid,current_user.gid,
269			 current_user.ngroups,current_user.groups)))) {
270		  /* We are allowed to become root and change the filetime. */
271		  become_root(False);
272		  ret = dos_utime(fname, times);
273		  unbecome_root(False);
274	  }
275  }
276
277  return ret;
278}
279
280/*******************************************************************
281Change a filetime - possibly allowing DOS semantics.
282*******************************************************************/
283BOOL set_filetime(connection_struct *conn, char *fname, time_t mtime)
284{
285  struct utimbuf times;
286
287  if (null_mtime(mtime)) return(True);
288
289  times.modtime = times.actime = mtime;
290
291  if (file_utime(conn, fname, &times)) {
292    DEBUG(4,("set_filetime(%s) failed: %s\n",fname,strerror(errno)));
293    return False;
294  }
295
296  return(True);
297}
298