s3: Add support for storing dos attributes as st_flags in the stat struct.
authorTim Prouty <tprouty@samba.org>
Wed, 5 Nov 2008 21:40:03 +0000 (13:40 -0800)
committerTim Prouty <tprouty@samba.org>
Wed, 12 Nov 2008 17:48:50 +0000 (09:48 -0800)
Some filesystems have support for storing dos attributes directly in
the inode's st_flags and accessing them through the stat struct.  This
patch:

- Adds a configure check to see if the special flags are available.
- Implements getting and setting dos attributes in the stat struct and
  inode, respectively.

This will not change the existing functionality of any system that
doesn't have the special flags available.

source3/configure.in
source3/smbd/dosmode.c

index 5e3eac55e2fa9e68928aeeceb079af14aa2ea07c..24341e0bec7304409f9d0a79426b044b7454d99e 100644 (file)
@@ -1433,6 +1433,25 @@ if test x"$samba_cv_stat_st_birthtime" = x"yes" ; then
     AC_DEFINE(HAVE_STAT_ST_BIRTHTIME, 1, [whether struct stat contains st_birthtime])
 fi
 
+AC_CACHE_CHECK([whether there is DOS flags support in the stat struct], samba_cv_stat_dos_flags,
+    [
+       AC_TRY_COMPILE(
+           [#include <sys/stat.h>],
+           [
+               int a = UF_DOS_ARCHIVE;
+               int h = UF_DOS_HIDDEN;
+               int r = UF_DOS_RO;
+               int s = UF_DOS_SYSTEM;
+               int i = UF_DOS_NOINDEX;
+               int f = UF_DOS_FLAGS;
+           ],
+           samba_cv_stat_dos_flags=yes, samba_cv_stat_dos_flags=no)
+    ])
+
+if test x"$samba_cv_stat_dos_flags" = x"yes" ; then
+    AC_DEFINE(HAVE_STAT_DOS_FLAGS, 1, [whether there is DOS flags support in the stat struct])
+fi
+
 #####################################
 # needed for SRV lookups
 AC_CHECK_LIB(resolv, dn_expand)
index 784b36d9bde0de62366a685466eef2c1d6b325bf..954cd5a4d27c0f102bd80ec8add71fc9d50e94ca 100644 (file)
@@ -30,6 +30,17 @@ static int set_sparse_flag(const SMB_STRUCT_STAT * const sbuf)
        return 0;
 }
 
+static int set_link_read_only_flag(const SMB_STRUCT_STAT *const sbuf)
+{
+#ifdef S_ISLNK
+#if LINKS_READ_ONLY
+       if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
+               return aRONLY;
+#endif
+#endif
+       return 0;
+}
+
 /****************************************************************************
  Change a dos mode to a unix mode.
     Base permission for files:
@@ -159,13 +170,7 @@ static uint32 dos_mode_from_sbuf(connection_struct *conn, const char *path, SMB_
                result = aDIR | (result & aRONLY);
 
        result |= set_sparse_flag(sbuf);
-#ifdef S_ISLNK
-#if LINKS_READ_ONLY
-       if (S_ISLNK(sbuf->st_mode) && S_ISDIR(sbuf->st_mode))
-               result |= aRONLY;
-#endif
-#endif
+       result |= set_link_read_only_flag(sbuf);
 
        DEBUG(8,("dos_mode_from_sbuf returning "));
 
@@ -343,6 +348,113 @@ uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT
        return(result);
 }
 
+#ifdef HAVE_STAT_DOS_FLAGS
+/****************************************************************************
+ Convert dos attributes (FILE_ATTRIBUTE_*) to dos stat flags (UF_*)
+****************************************************************************/
+
+static int dos_attributes_to_stat_dos_flags(uint32_t dosmode)
+{
+       uint32_t dos_stat_flags = 0;
+
+       if (dosmode & aARCH)
+               dos_stat_flags |= UF_DOS_ARCHIVE;
+       if (dosmode & aHIDDEN)
+               dos_stat_flags |= UF_DOS_HIDDEN;
+       if (dosmode & aRONLY)
+               dos_stat_flags |= UF_DOS_RO;
+       if (dosmode & aSYSTEM)
+               dos_stat_flags |= UF_DOS_SYSTEM;
+       if (dosmode & FILE_ATTRIBUTE_NONINDEXED)
+               dos_stat_flags |= UF_DOS_NOINDEX;
+
+       return dos_stat_flags;
+}
+
+/****************************************************************************
+ Gets DOS attributes, accessed via st_flags in the stat struct.
+****************************************************************************/
+
+static bool get_stat_dos_flags(connection_struct *conn,
+                              const char *fname,
+                              const SMB_STRUCT_STAT *sbuf,
+                              uint32_t *dosmode)
+{
+       SMB_ASSERT(sbuf && VALID_STAT(*sbuf));
+       SMB_ASSERT(dosmode);
+
+       if (!lp_store_dos_attributes(SNUM(conn))) {
+               return false;
+       }
+
+       DEBUG(5, ("Getting stat dos attributes for %s.\n", fname));
+
+       if (sbuf->st_flags & UF_DOS_ARCHIVE)
+               *dosmode |= aARCH;
+       if (sbuf->st_flags & UF_DOS_HIDDEN)
+               *dosmode |= aHIDDEN;
+       if (sbuf->st_flags & UF_DOS_RO)
+               *dosmode |= aRONLY;
+       if (sbuf->st_flags & UF_DOS_SYSTEM)
+               *dosmode |= aSYSTEM;
+       if (sbuf->st_flags & UF_DOS_NOINDEX)
+               *dosmode |= FILE_ATTRIBUTE_NONINDEXED;
+       if (S_ISDIR(sbuf->st_mode))
+               *dosmode |= aDIR;
+
+       *dosmode |= set_sparse_flag(sbuf);
+       *dosmode |= set_link_read_only_flag(sbuf);
+
+       return true;
+}
+
+/****************************************************************************
+ Sets DOS attributes, stored in st_flags of the inode.
+****************************************************************************/
+
+static bool set_stat_dos_flags(connection_struct *conn,
+                               const char *fname,
+                               SMB_STRUCT_STAT *sbuf,
+                               uint32_t dosmode,
+                               bool *attributes_changed)
+{
+       uint32_t new_flags = 0;
+       int error = 0;
+
+       SMB_ASSERT(sbuf && VALID_STAT(*sbuf));
+       SMB_ASSERT(attributes_changed);
+
+       *attributes_changed = false;
+
+       if (!lp_store_dos_attributes(SNUM(conn))) {
+               return false;
+       }
+
+       DEBUG(5, ("Setting stat dos attributes for %s.\n", fname));
+
+       new_flags = (sbuf->st_flags & ~UF_DOS_FLAGS) |
+                    dos_attributes_to_stat_dos_flags(dosmode);
+
+       /* Return early if no flags changed. */
+       if (new_flags == sbuf->st_flags)
+               return true;
+
+       DEBUG(5, ("Setting stat dos attributes=0x%x, prev=0x%x\n", new_flags,
+                 sbuf->st_flags));
+
+       /* Set new flags with chflags. */
+       error = SMB_VFS_CHFLAGS(conn, fname, new_flags);
+       if (error) {
+               DEBUG(0, ("Failed setting new stat dos attributes (0x%x) on "
+                         "file %s! errno=%d\n", new_flags, fname, errno));
+               return false;
+       }
+
+       *attributes_changed = true;
+       return true;
+}
+#endif /* HAVE_STAT_DOS_FLAGS */
+
 /****************************************************************************
  Change a unix mode to a dos mode.
 ****************************************************************************/
@@ -350,7 +462,7 @@ uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT
 uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
 {
        uint32 result = 0;
-       bool offline;
+       bool offline, used_stat_dos_flags = false;
 
        DEBUG(8,("dos_mode: %s\n", path));
 
@@ -373,11 +485,16 @@ uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf)
                }
        }
        
-       /* Get the DOS attributes from an EA by preference. */
-       if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
-               result |= set_sparse_flag(sbuf);
-       } else {
-               result |= dos_mode_from_sbuf(conn, path, sbuf);
+#ifdef HAVE_STAT_DOS_FLAGS
+       used_stat_dos_flags = get_stat_dos_flags(conn, path, sbuf, &result);
+#endif
+       if (!used_stat_dos_flags) {
+               /* Get the DOS attributes from an EA by preference. */
+               if (get_ea_dos_attribute(conn, path, sbuf, &result)) {
+                       result |= set_sparse_flag(sbuf);
+               } else {
+                       result |= dos_mode_from_sbuf(conn, path, sbuf);
+               }
        }
 
        
@@ -468,6 +585,23 @@ int file_set_dosmode(connection_struct *conn, const char *fname,
                return(0);
        }
 
+#ifdef HAVE_STAT_DOS_FLAGS
+       {
+               bool attributes_changed;
+
+               if (set_stat_dos_flags(conn, fname, st, dosmode,
+                                      &attributes_changed))
+               {
+                       if (!newfile && attributes_changed) {
+                               notify_fname(conn, NOTIFY_ACTION_MODIFIED,
+                                   FILE_NOTIFY_CHANGE_ATTRIBUTES, fname);
+                       }
+                       st->st_mode = unixmode;
+                       return 0;
+               }
+       }
+#endif
+
        /* Store the DOS attributes in an EA by preference. */
        if (set_ea_dos_attribute(conn, fname, st, dosmode)) {
                if (!newfile) {