vfs_fileid: add "fstype/mntdir deny/allow list" option
authorRalph Wuerthner <ralph.wuerthner@de.ibm.com>
Tue, 12 Jan 2016 15:00:24 +0000 (16:00 +0100)
committerJeremy Allison <jra@samba.org>
Fri, 5 Jan 2018 23:07:17 +0000 (00:07 +0100)
When using the fsname or fsid algorithm a stat() and statfs() call is
required for all mounted file systems to generate the file_id. If e.g.
an NFS file system is unresponsive such a call might block and the smbd
process will become unresponsive. Add "fileid:fstype deny",
"fileid:fstype allow", "fileid:mntdir deny", and "fileid:mntdir allow"
options to ignore potentially unresponsive file systems.

See also https://lists.samba.org/archive/samba-technical/2016-January/111553.html
for a discussion about why this is useful.

Signed-off-by: Ralph Wuerthner <ralph.wuerthner@de.ibm.com>
Reviewed-by: Ralph Boehme <slow@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
docs-xml/manpages/vfs_fileid.8.xml
source3/modules/vfs_fileid.c

index 5a3a70e9b95119940957b7c3382ba47b11894c07..568756ba519e3f5b301b50458ae686058f798a75 100644 (file)
        generates the device number based on the configured algorithm
        (see the "fileid:algorithm" option).
        </para>
+
+       <para>When using the fsname or fsid algorithm a
+       <command>stat()</command> and <command>statfs()</command> call is
+       required for all mounted file systems to generate the file_id. If e.g.
+       an NFS file system is unresponsive such a call might block and the smbd
+       process will become unresponsive. Use the "fileid:fstype deny",
+       "fileid:fstype allow", "fileid:mntdir deny", or "fileid:mntdir allow"
+       options to ignore potentially unresponsive file systems.
+       </para>
 </refsect1>
 
 
                </listitem>
                </varlistentry>
 
+               <varlistentry>
+               <term>fileid:fstype deny = LIST</term>
+               <listitem>
+               <para>List of file system types to be ignored for file_id
+               generation.
+               </para>
+               </listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term>fileid:fstype allow = LIST</term>
+               <listitem>
+               <para>List of file system types to be allowed for file_id
+               generation. If this option is set, file system types not listed
+               here are ignored.
+               </para>
+               </listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term>fileid:mntdir deny = LIST</term>
+               <listitem>
+               <para>List of file system mount points to be ignored for
+               file_id generation.
+               </para>
+               </listitem>
+               </varlistentry>
+
+               <varlistentry>
+               <term>fileid:mntdir allow = LIST</term>
+               <listitem>
+               <para>List of file system mount points to be allowed for file_id
+               generation. If this option is set, file system mount points
+               not listed here are ignored.
+               </para>
+               </listitem>
+               </varlistentry>
+
        </variablelist>
 </refsect1>
 
index fedcac7e6d560c4b4e4252552f8150be7be566a0..583665775b263a0f0dc0d320fca2d7bcc7bf35ea 100644 (file)
@@ -38,10 +38,62 @@ struct fileid_mount_entry {
 struct fileid_handle_data {
        uint64_t (*device_mapping_fn)(struct fileid_handle_data *data,
                                      SMB_DEV_T dev);
+       char **fstype_deny_list;
+       char **fstype_allow_list;
+       char **mntdir_deny_list;
+       char **mntdir_allow_list;
        unsigned num_mount_entries;
        struct fileid_mount_entry *mount_entries;
 };
 
+/* check if a mount entry is allowed based on fstype and mount directory */
+static bool fileid_mount_entry_allowed(struct fileid_handle_data *data,
+                                      struct mntent *m)
+{
+       int i;
+       char **fstype_deny = data->fstype_deny_list;
+       char **fstype_allow = data->fstype_allow_list;
+       char **mntdir_deny = data->mntdir_deny_list;
+       char **mntdir_allow = data->mntdir_allow_list;
+
+       if (fstype_deny != NULL) {
+               for (i = 0; fstype_deny[i] != NULL; i++) {
+                       if (strcmp(m->mnt_type, fstype_deny[i]) == 0) {
+                               return false;
+                       }
+               }
+       }
+       if (fstype_allow != NULL) {
+               for (i = 0; fstype_allow[i] != NULL; i++) {
+                       if (strcmp(m->mnt_type, fstype_allow[i]) == 0) {
+                               break;
+                       }
+               }
+               if (fstype_allow[i] == NULL) {
+                       return false;
+               }
+       }
+       if (mntdir_deny != NULL) {
+               for (i=0; mntdir_deny[i] != NULL; i++) {
+                       if (strcmp(m->mnt_dir, mntdir_deny[i]) == 0) {
+                               return false;
+                       }
+               }
+       }
+       if (mntdir_allow != NULL) {
+               for (i=0; mntdir_allow[i] != NULL; i++) {
+                       if (strcmp(m->mnt_dir, mntdir_allow[i]) == 0) {
+                               break;
+                       }
+               }
+               if (mntdir_allow[i] == NULL) {
+                       return false;
+               }
+       }
+       return true;
+}
+
+
 /* load all the mount entries from the mtab */
 static void fileid_load_mount_entries(struct fileid_handle_data *data)
 {
@@ -58,7 +110,13 @@ static void fileid_load_mount_entries(struct fileid_handle_data *data)
                struct stat st;
                struct statfs sfs;
                struct fileid_mount_entry *cur;
+               bool allowed;
 
+               allowed = fileid_mount_entry_allowed(data, m);
+               if (!allowed) {
+                       DBG_DEBUG("skipping mount entry %s\n", m->mnt_dir);
+                       continue;
+               }
                if (stat(m->mnt_dir, &st) != 0) continue;
                if (statfs(m->mnt_dir, &sfs) != 0) continue;
 
@@ -183,6 +241,10 @@ static int fileid_connect(struct vfs_handle_struct *handle,
 {
        struct fileid_handle_data *data;
        const char *algorithm;
+       const char **fstype_deny_list = NULL;
+       const char **fstype_allow_list = NULL;
+       const char **mntdir_deny_list = NULL;
+       const char **mntdir_allow_list = NULL;
        int saved_errno;
        int ret = SMB_VFS_NEXT_CONNECT(handle, service, user);
 
@@ -219,6 +281,58 @@ static int fileid_connect(struct vfs_handle_struct *handle,
                return -1;
        }
 
+       fstype_deny_list = lp_parm_string_list(SNUM(handle->conn), "fileid",
+                                              "fstype deny", NULL);
+       if (fstype_deny_list != NULL) {
+               data->fstype_deny_list = str_list_copy(data, fstype_deny_list);
+               if (data->fstype_deny_list == NULL) {
+                       saved_errno = errno;
+                       DBG_ERR("str_list_copy failed\n");
+                       SMB_VFS_NEXT_DISCONNECT(handle);
+                       errno = saved_errno;
+                       return -1;
+               }
+       }
+
+       fstype_allow_list = lp_parm_string_list(SNUM(handle->conn), "fileid",
+                                               "fstype allow", NULL);
+       if (fstype_allow_list != NULL) {
+               data->fstype_allow_list = str_list_copy(data, fstype_allow_list);
+               if (data->fstype_allow_list == NULL) {
+                       saved_errno = errno;
+                       DBG_ERR("str_list_copy failed\n");
+                       SMB_VFS_NEXT_DISCONNECT(handle);
+                       errno = saved_errno;
+                       return -1;
+               }
+       }
+
+       mntdir_deny_list = lp_parm_string_list(SNUM(handle->conn), "fileid",
+                                              "mntdir deny", NULL);
+       if (mntdir_deny_list != NULL) {
+               data->mntdir_deny_list = str_list_copy(data, mntdir_deny_list);
+               if (data->mntdir_deny_list == NULL) {
+                       saved_errno = errno;
+                       DBG_ERR("str_list_copy failed\n");
+                       SMB_VFS_NEXT_DISCONNECT(handle);
+                       errno = saved_errno;
+                       return -1;
+               }
+       }
+
+       mntdir_allow_list = lp_parm_string_list(SNUM(handle->conn), "fileid",
+                                               "mntdir allow", NULL);
+       if (mntdir_allow_list != NULL) {
+               data->mntdir_allow_list = str_list_copy(data, mntdir_allow_list);
+               if (data->mntdir_allow_list == NULL) {
+                       saved_errno = errno;
+                       DBG_ERR("str_list_copy failed\n");
+                       SMB_VFS_NEXT_DISCONNECT(handle);
+                       errno = saved_errno;
+                       return -1;
+               }
+       }
+
        SMB_VFS_HANDLE_SET_DATA(handle, data, NULL,
                                struct fileid_handle_data,
                                return -1);