Prefix VFS API macros with SMB_ for consistency and to avoid problems with VFS_ macro...
[tprouty/samba.git] / source / smbd / dosmode.c
index 1ae1e7e2cb1e9d8024650b165ea71e447368d22b..aaee41b546a965d75e15b25bda8d0d534f5d3458 100644 (file)
@@ -1,6 +1,5 @@
 /* 
-   Unix SMB/Netbios implementation.
-   Version 1.9.
+   Unix SMB/CIFS implementation.
    dos mode handling functions
    Copyright (C) Andrew Tridgell 1992-1998
    
@@ -21,8 +20,6 @@
 
 #include "includes.h"
 
-extern int DEBUGLEVEL;
-
 /****************************************************************************
   change a dos mode to a unix mode
     base permission for files:
@@ -59,7 +56,7 @@ mode_t unix_mode(connection_struct *conn,int dosmode,const char *fname)
 
     dname = parent_dirname(fname);
     DEBUG(2,("unix_mode(%s) inheriting from %s\n",fname,dname));
-    if (dos_stat(dname,&sbuf) != 0) {
+    if (SMB_VFS_STAT(conn,dname,&sbuf) != 0) {
       DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",fname,dname,strerror(errno)));
       return(0);      /* *** shouldn't happen! *** */
     }
@@ -109,6 +106,8 @@ mode_t unix_mode(connection_struct *conn,int dosmode,const char *fname)
       result |= lp_force_create_mode(SNUM(conn));
     }
   }
+
+  DEBUG(3,("unix_mode(%s) returning 0%o\n",fname,(int)result ));
   return(result);
 }
 
@@ -116,116 +115,159 @@ mode_t unix_mode(connection_struct *conn,int dosmode,const char *fname)
 /****************************************************************************
   change a unix mode to a dos mode
 ****************************************************************************/
-int dos_mode(connection_struct *conn,char *path,SMB_STRUCT_STAT *sbuf)
+uint32 dos_mode(connection_struct *conn,char *path,SMB_STRUCT_STAT *sbuf)
 {
-  int result = 0;
-
-  DEBUG(8,("dos_mode: %s\n", path));
+       int result = 0;
 
-  if ((sbuf->st_mode & S_IWUSR) == 0)
-      result |= aRONLY;
+       DEBUG(8,("dos_mode: %s\n", path));
 
-  if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
-    result |= aARCH;
+       if ((sbuf->st_mode & S_IWUSR) == 0)
+               result |= aRONLY;
+       
+       if (MAP_ARCHIVE(conn) && ((sbuf->st_mode & S_IXUSR) != 0))
+               result |= aARCH;
 
-  if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
-    result |= aSYSTEM;
-
-  if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
-    result |= aHIDDEN;   
+       if (MAP_SYSTEM(conn) && ((sbuf->st_mode & S_IXGRP) != 0))
+               result |= aSYSTEM;
+       
+       if (MAP_HIDDEN(conn) && ((sbuf->st_mode & S_IXOTH) != 0))
+               result |= aHIDDEN;   
   
-  if (S_ISDIR(sbuf->st_mode))
-    result = aDIR | (result & aRONLY);
+       if (S_ISDIR(sbuf->st_mode))
+               result = aDIR | (result & aRONLY);
 
+       if (sbuf->st_size > sbuf->st_blocks * (SMB_OFF_T)sbuf->st_blksize) {
+               result |= FILE_ATTRIBUTE_SPARSE;
+       }
 #ifdef S_ISLNK
 #if LINKS_READ_ONLY
-  if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
-    result |= aRONLY;
+       if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
+               result |= aRONLY;
 #endif
 #endif
 
-  /* hide files with a name starting with a . */
-  if (lp_hide_dot_files(SNUM(conn)))
-    {
-      char *p = strrchr(path,'/');
-      if (p)
-       p++;
-      else
-       p = path;
-      
-      if (p[0] == '.' && p[1] != '.' && p[1] != 0)
-       result |= aHIDDEN;
-    }
-
-  /* Optimization : Only call is_hidden_path if it's not already
-     hidden. */
-  if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path))
-  {
-    result |= aHIDDEN;
-  }
-
-  DEBUG(8,("dos_mode returning "));
-
-  if (result & aHIDDEN) DEBUG(8, ("h"));
-  if (result & aRONLY ) DEBUG(8, ("r"));
-  if (result & aSYSTEM) DEBUG(8, ("s"));
-  if (result & aDIR   ) DEBUG(8, ("d"));
-  if (result & aARCH  ) DEBUG(8, ("a"));
-
-  DEBUG(8,("\n"));
-
-  return(result);
+       /* hide files with a name starting with a . */
+       if (lp_hide_dot_files(SNUM(conn))) {
+               char *p = strrchr_m(path,'/');
+               if (p)
+                       p++;
+               else
+                       p = path;
+               
+               if (p[0] == '.' && p[1] != '.' && p[1] != 0)
+                       result |= aHIDDEN;
+       }
+       
+       /* Optimization : Only call is_hidden_path if it's not already
+          hidden. */
+       if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) {
+               result |= aHIDDEN;
+       }
+
+       DEBUG(8,("dos_mode returning "));
+
+       if (result & aHIDDEN) DEBUG(8, ("h"));
+       if (result & aRONLY ) DEBUG(8, ("r"));
+       if (result & aSYSTEM) DEBUG(8, ("s"));
+       if (result & aDIR   ) DEBUG(8, ("d"));
+       if (result & aARCH  ) DEBUG(8, ("a"));
+       
+       DEBUG(8,("\n"));
+
+       return(result);
 }
 
 /*******************************************************************
 chmod a file - but preserve some bits
 ********************************************************************/
-int file_chmod(connection_struct *conn,char *fname,int dosmode,SMB_STRUCT_STAT *st)
+int file_chmod(connection_struct *conn,char *fname, uint32 dosmode,SMB_STRUCT_STAT *st)
 {
-  SMB_STRUCT_STAT st1;
-  int mask=0;
-  mode_t tmp;
-  mode_t unixmode;
-
-  if (!st) {
-    st = &st1;
-    if (conn->vfs_ops.stat(dos_to_unix(fname,False),st)) return(-1);
-  }
+       SMB_STRUCT_STAT st1;
+       int mask=0;
+       mode_t tmp;
+       mode_t unixmode;
+       int ret = -1;
 
-  if (S_ISDIR(st->st_mode)) dosmode |= aDIR;
+       if (!st) {
+               st = &st1;
+               if (SMB_VFS_STAT(conn,fname,st))
+                       return(-1);
+       }
 
-  if (dos_mode(conn,fname,st) == dosmode) return(0);
+       if (S_ISDIR(st->st_mode))
+               dosmode |= aDIR;
+       else
+               dosmode &= ~aDIR;
 
-  unixmode = unix_mode(conn,dosmode,fname);
+       if (dos_mode(conn,fname,st) == dosmode)
+               return(0);
 
-  /* preserve the s bits */
-  mask |= (S_ISUID | S_ISGID);
+       unixmode = unix_mode(conn,dosmode,fname);
 
-  /* preserve the t bit */
+       /* preserve the s bits */
+       mask |= (S_ISUID | S_ISGID);
+
+       /* preserve the t bit */
 #ifdef S_ISVTX
-  mask |= S_ISVTX;
+       mask |= S_ISVTX;
 #endif
 
-  /* possibly preserve the x bits */
-  if (!MAP_ARCHIVE(conn)) mask |= S_IXUSR;
-  if (!MAP_SYSTEM(conn)) mask |= S_IXGRP;
-  if (!MAP_HIDDEN(conn)) mask |= S_IXOTH;
-
-  unixmode |= (st->st_mode & mask);
-
-  /* if we previously had any r bits set then leave them alone */
-  if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
-    unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
-    unixmode |= tmp;
-  }
-
-  /* if we previously had any w bits set then leave them alone 
-   whilst adding in the new w bits, if the new mode is not rdonly */
-  if (!IS_DOS_READONLY(dosmode)) {
-    unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
-  }
-
-  return(conn->vfs_ops.chmod(fname,unixmode));
+       /* possibly preserve the x bits */
+       if (!MAP_ARCHIVE(conn))
+               mask |= S_IXUSR;
+       if (!MAP_SYSTEM(conn))
+               mask |= S_IXGRP;
+       if (!MAP_HIDDEN(conn))
+               mask |= S_IXOTH;
+
+       unixmode |= (st->st_mode & mask);
+
+       /* if we previously had any r bits set then leave them alone */
+       if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) {
+               unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH);
+               unixmode |= tmp;
+       }
+
+       /* if we previously had any w bits set then leave them alone 
+               whilst adding in the new w bits, if the new mode is not rdonly */
+       if (!IS_DOS_READONLY(dosmode)) {
+               unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH));
+       }
+
+       if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0)
+               return 0;
+
+       if((errno != EPERM) && (errno != EACCES))
+               return -1;
+
+       if(!lp_dos_filemode(SNUM(conn)))
+               return -1;
+
+       /* We want DOS semantics, ie allow non owner with write permission to change the
+               bits on a file. Just like file_utime below.
+       */
+
+       /* Check if we have write access. */
+       if (CAN_WRITE(conn)) {
+               /*
+                * We need to open the file with write access whilst
+                * still in our current user context. This ensures we
+                * are not violating security in doing the fchmod.
+                * This file open does *not* break any oplocks we are
+                * holding. We need to review this.... may need to
+                * break batch oplocks open by others. JRA.
+                */
+               files_struct *fsp = open_file_fchmod(conn,fname,st);
+               if (!fsp)
+                       return -1;
+               become_root();
+               ret = SMB_VFS_FCHMOD(fsp, fsp->fd, unixmode);
+               unbecome_root();
+               close_file_fchmod(fsp);
+       }
+
+       return( ret );
 }
 
 
@@ -241,7 +283,7 @@ int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
 
   errno = 0;
 
-  if(conn->vfs_ops.utime(dos_to_unix(fname, False), times) == 0)
+  if(SMB_VFS_UTIME(conn,fname, times) == 0)
     return 0;
 
   if((errno != EPERM) && (errno != EACCES))
@@ -256,7 +298,7 @@ int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
      (as DOS does).
    */
 
-  if(conn->vfs_ops.stat(dos_to_unix(fname,False),&sb) != 0)
+  if(SMB_VFS_STAT(conn,fname,&sb) != 0)
     return -1;
 
   /* Check if we have write access. */
@@ -268,9 +310,9 @@ int file_utime(connection_struct *conn, char *fname, struct utimbuf *times)
                in_group(sb.st_gid,current_user.gid,
                         current_user.ngroups,current_user.groups)))) {
                  /* We are allowed to become root and change the filetime. */
-                 become_root(False);
-                 ret = conn->vfs_ops.utime(dos_to_unix(fname, False), times);
-                 unbecome_root(False);
+                 become_root();
+                 ret = SMB_VFS_UTIME(conn,fname, times);
+                 unbecome_root();
          }
   }