LCOV - code coverage report
Current view: top level - source3/modules - vfs_dirsort.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 106 131 80.9 %
Date: 2021-09-23 10:06:22 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /*
       2             :  * VFS module to provide a sorted directory list.
       3             :  *
       4             :  * Copyright (C) Andy Kelk (andy@mopoke.co.uk), 2009
       5             :  *
       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 3 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, see <http://www.gnu.org/licenses/>.
      19             :  */
      20             : 
      21             : #include "includes.h"
      22             : #include "smbd/smbd.h"
      23             : #include "system/filesys.h"
      24             : 
      25    13580979 : static int compare_dirent (const struct dirent *da, const struct dirent *db)
      26             : {
      27    13580979 :         return strcasecmp_m(da->d_name, db->d_name);
      28             : }
      29             : 
      30             : struct dirsort_privates {
      31             :         struct dirsort_privates *prev, *next;
      32             :         long pos;
      33             :         struct dirent *directory_list;
      34             :         unsigned int number_of_entries;
      35             :         struct timespec mtime;
      36             :         DIR *source_directory;
      37             :         files_struct *fsp; /* If open via FDOPENDIR. */
      38             :         struct smb_filename *smb_fname; /* If open via OPENDIR */
      39             : };
      40             : 
      41     1565420 : static bool get_sorted_dir_mtime(vfs_handle_struct *handle,
      42             :                                 struct dirsort_privates *data,
      43             :                                 struct timespec *ret_mtime)
      44             : {
      45             :         int ret;
      46             :         struct timespec mtime;
      47             :         NTSTATUS status;
      48             : 
      49     1565420 :         if (data->fsp) {
      50     1565420 :                 status = vfs_stat_fsp(data->fsp);
      51     1565420 :                 if (!NT_STATUS_IS_OK(status)) {
      52           0 :                         return false;
      53             :                 }
      54     1565420 :                 mtime = data->fsp->fsp_name->st.st_ex_mtime;
      55             :         } else {
      56           0 :                 ret = SMB_VFS_STAT(handle->conn, data->smb_fname);
      57           0 :                 if (ret == -1) {
      58           0 :                         return false;
      59             :                 }
      60           0 :                 mtime = data->smb_fname->st.st_ex_mtime;
      61             :         }
      62             : 
      63     1565420 :         *ret_mtime = mtime;
      64             : 
      65     1565420 :         return true;
      66             : }
      67             : 
      68        6832 : static bool open_and_sort_dir(vfs_handle_struct *handle,
      69             :                                 struct dirsort_privates *data)
      70             : {
      71        6832 :         uint32_t total_count = 0;
      72             :         /* This should be enough for most use cases */
      73        6832 :         uint32_t dirent_allocated = 64;
      74             :         struct dirent *dp;
      75             : 
      76        6832 :         data->number_of_entries = 0;
      77             : 
      78        6832 :         if (get_sorted_dir_mtime(handle, data, &data->mtime) == false) {
      79           0 :                 return false;
      80             :         }
      81             : 
      82        6832 :         dp = SMB_VFS_NEXT_READDIR(handle,
      83             :                                   data->fsp,
      84             :                                   data->source_directory,
      85             :                                   NULL);
      86        6832 :         if (dp == NULL) {
      87           0 :                 return false;
      88             :         }
      89             : 
      90             :         /* Set up an array and read the directory entries into it */
      91        6832 :         TALLOC_FREE(data->directory_list); /* destroy previous cache if needed */
      92        6832 :         data->directory_list = talloc_zero_array(data,
      93             :                                                  struct dirent,
      94             :                                                  dirent_allocated);
      95        6832 :         if (data->directory_list == NULL) {
      96           0 :                 return false;
      97             :         }
      98             : 
      99             :         do {
     100     2385134 :                 if (total_count >= dirent_allocated) {
     101             :                         struct dirent *dlist;
     102             : 
     103             :                         /*
     104             :                          * Be memory friendly.
     105             :                          *
     106             :                          * We should not double the amount of memory. With a lot
     107             :                          * of files we reach easily 50MB, and doubling will
     108             :                          * get much bigger just for a few files more.
     109             :                          *
     110             :                          * For 200k files this means 50 memory reallocations.
     111             :                          */
     112        5738 :                         dirent_allocated += 4096;
     113             : 
     114        5738 :                         dlist = talloc_realloc(data,
     115             :                                                data->directory_list,
     116             :                                                struct dirent,
     117             :                                                dirent_allocated);
     118        5738 :                         if (dlist == NULL) {
     119           0 :                                 break;
     120             :                         }
     121        5738 :                         data->directory_list = dlist;
     122             :                 }
     123     2385134 :                 data->directory_list[total_count] = *dp;
     124             : 
     125     2385134 :                 total_count++;
     126     2385134 :                 dp = SMB_VFS_NEXT_READDIR(handle,
     127             :                                           data->fsp,
     128             :                                           data->source_directory,
     129             :                                           NULL);
     130     2385134 :         } while (dp != NULL);
     131             : 
     132        6832 :         data->number_of_entries = total_count;
     133             : 
     134             :         /* Sort the directory entries by name */
     135        6832 :         TYPESAFE_QSORT(data->directory_list, data->number_of_entries, compare_dirent);
     136        6832 :         return true;
     137             : }
     138             : 
     139        4976 : static DIR *dirsort_fdopendir(vfs_handle_struct *handle,
     140             :                                         files_struct *fsp,
     141             :                                         const char *mask,
     142             :                                         uint32_t attr)
     143             : {
     144        4976 :         struct dirsort_privates *list_head = NULL;
     145        4976 :         struct dirsort_privates *data = NULL;
     146             : 
     147        4976 :         if (SMB_VFS_HANDLE_TEST_DATA(handle)) {
     148             :                 /* Find the list head of all open directories. */
     149         114 :                 SMB_VFS_HANDLE_GET_DATA(handle, list_head, struct dirsort_privates,
     150             :                                 return NULL);
     151             :         }
     152             : 
     153             :         /* set up our private data about this directory */
     154        4976 :         data = talloc_zero(handle->conn, struct dirsort_privates);
     155        4976 :         if (!data) {
     156           0 :                 return NULL;
     157             :         }
     158             : 
     159        4976 :         data->fsp = fsp;
     160             : 
     161             :         /* Open the underlying directory and count the number of entries */
     162        4976 :         data->source_directory = SMB_VFS_NEXT_FDOPENDIR(handle, fsp, mask,
     163             :                                                       attr);
     164             : 
     165        4976 :         if (data->source_directory == NULL) {
     166           0 :                 TALLOC_FREE(data);
     167           0 :                 return NULL;
     168             :         }
     169             : 
     170        4976 :         if (!open_and_sort_dir(handle, data)) {
     171           0 :                 SMB_VFS_NEXT_CLOSEDIR(handle,data->source_directory);
     172           0 :                 TALLOC_FREE(data);
     173             :                 /* fd is now closed. */
     174           0 :                 fsp_set_fd(fsp, -1);
     175           0 :                 return NULL;
     176             :         }
     177             : 
     178             :         /* Add to the private list of all open directories. */
     179        4976 :         DLIST_ADD(list_head, data);
     180        4976 :         SMB_VFS_HANDLE_SET_DATA(handle, list_head, NULL,
     181             :                                 struct dirsort_privates, return NULL);
     182             : 
     183        4976 :         return data->source_directory;
     184             : }
     185             : 
     186     1558200 : static struct dirent *dirsort_readdir(vfs_handle_struct *handle,
     187             :                                       struct files_struct *dirfsp,
     188             :                                       DIR *dirp,
     189             :                                       SMB_STRUCT_STAT *sbuf)
     190             : {
     191     1558200 :         struct dirsort_privates *data = NULL;
     192             :         struct timespec current_mtime;
     193             : 
     194     1558200 :         SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates,
     195             :                                 return NULL);
     196             : 
     197     3117160 :         while(data && (data->source_directory != dirp)) {
     198         760 :                 data = data->next;
     199             :         }
     200     1558200 :         if (data == NULL) {
     201           0 :                 return NULL;
     202             :         }
     203             : 
     204     1558200 :         if (get_sorted_dir_mtime(handle, data, &current_mtime) == false) {
     205           0 :                 return NULL;
     206             :         }
     207             : 
     208             :         /* throw away cache and re-read the directory if we've changed */
     209     1558200 :         if (timespec_compare(&current_mtime, &data->mtime)) {
     210        1508 :                 SMB_VFS_NEXT_REWINDDIR(handle, data->source_directory);
     211        1508 :                 open_and_sort_dir(handle, data);
     212             :         }
     213             : 
     214     1558200 :         if (data->pos >= data->number_of_entries) {
     215        4944 :                 return NULL;
     216             :         }
     217             : 
     218     1553256 :         return &data->directory_list[data->pos++];
     219             : }
     220             : 
     221         390 : static void dirsort_seekdir(vfs_handle_struct *handle, DIR *dirp,
     222             :                             long offset)
     223             : {
     224             :         struct timespec current_mtime;
     225         390 :         struct dirsort_privates *data = NULL;
     226             : 
     227         392 :         SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates, return);
     228             : 
     229             :         /* Find the entry holding dirp. */
     230        1160 :         while(data && (data->source_directory != dirp)) {
     231         380 :                 data = data->next;
     232             :         }
     233         390 :         if (data == NULL) {
     234           0 :                 return;
     235             :         }
     236         390 :         if (offset >= data->number_of_entries) {
     237           2 :                 return;
     238             :         }
     239         388 :         data->pos = offset;
     240             : 
     241         388 :         if (get_sorted_dir_mtime(handle, data, &current_mtime) == false) {
     242           0 :                 return;
     243             :         }
     244             : 
     245         388 :         if (timespec_compare(&current_mtime, &data->mtime)) {
     246             :                 /* Directory changed. We must re-read the
     247             :                    cache and search for the name that was
     248             :                    previously stored at the offset being
     249             :                    requested, otherwise after the re-sort
     250             :                    we will point to the wrong entry. The
     251             :                    OS/2 incremental delete code relies on
     252             :                    this. */
     253             :                 unsigned int i;
     254         348 :                 char *wanted_name = talloc_strdup(handle->conn,
     255         348 :                                         data->directory_list[offset].d_name);
     256         348 :                 if (wanted_name == NULL) {
     257           0 :                         return;
     258             :                 }
     259         348 :                 SMB_VFS_NEXT_REWINDDIR(handle, data->source_directory);
     260         348 :                 open_and_sort_dir(handle, data);
     261             :                 /* Now search for where we were. */
     262         348 :                 data->pos = 0;
     263        1044 :                 for (i = 0; i < data->number_of_entries; i++) {
     264        1044 :                         if(strcmp(wanted_name, data->directory_list[i].d_name) == 0) {
     265         348 :                                 data->pos = i;
     266         348 :                                 break;
     267             :                         }
     268             :                 }
     269         348 :                 TALLOC_FREE(wanted_name);
     270             :         }
     271             : }
     272             : 
     273     1543348 : static long dirsort_telldir(vfs_handle_struct *handle, DIR *dirp)
     274             : {
     275     1543348 :         struct dirsort_privates *data = NULL;
     276     1543348 :         SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates,
     277             :                                 return -1);
     278             : 
     279             :         /* Find the entry holding dirp. */
     280     3087456 :         while(data && (data->source_directory != dirp)) {
     281         760 :                 data = data->next;
     282             :         }
     283     1543348 :         if (data == NULL) {
     284           0 :                 return -1;
     285             :         }
     286     1543348 :         return data->pos;
     287             : }
     288             : 
     289           2 : static void dirsort_rewinddir(vfs_handle_struct *handle, DIR *dirp)
     290             : {
     291           2 :         struct dirsort_privates *data = NULL;
     292           2 :         SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates, return);
     293             : 
     294             :         /* Find the entry holding dirp. */
     295           4 :         while(data && (data->source_directory != dirp)) {
     296           0 :                 data = data->next;
     297             :         }
     298           2 :         if (data == NULL) {
     299           0 :                 return;
     300             :         }
     301           2 :         data->pos = 0;
     302             : }
     303             : 
     304        4976 : static int dirsort_closedir(vfs_handle_struct *handle, DIR *dirp)
     305             : {
     306        4976 :         struct dirsort_privates *list_head = NULL;
     307        4976 :         struct dirsort_privates *data = NULL;
     308             :         int ret;
     309             : 
     310        4976 :         SMB_VFS_HANDLE_GET_DATA(handle, list_head, struct dirsort_privates, return -1);
     311             :         /* Find the entry holding dirp. */
     312        4976 :         for(data = list_head; data && (data->source_directory != dirp); data = data->next) {
     313             :                 ;
     314             :         }
     315        4976 :         if (data == NULL) {
     316           0 :                 return -1;
     317             :         }
     318             :         /* Remove from the list and re-store the list head. */
     319        4976 :         DLIST_REMOVE(list_head, data);
     320        4976 :         SMB_VFS_HANDLE_SET_DATA(handle, list_head, NULL,
     321             :                                 struct dirsort_privates, return -1);
     322             : 
     323        4976 :         ret = SMB_VFS_NEXT_CLOSEDIR(handle, dirp);
     324        4976 :         TALLOC_FREE(data);
     325        4976 :         return ret;
     326             : }
     327             : 
     328             : static struct vfs_fn_pointers vfs_dirsort_fns = {
     329             :         .fdopendir_fn = dirsort_fdopendir,
     330             :         .readdir_fn = dirsort_readdir,
     331             :         .seekdir_fn = dirsort_seekdir,
     332             :         .telldir_fn = dirsort_telldir,
     333             :         .rewind_dir_fn = dirsort_rewinddir,
     334             :         .closedir_fn = dirsort_closedir,
     335             : };
     336             : 
     337             : static_decl_vfs;
     338         240 : NTSTATUS vfs_dirsort_init(TALLOC_CTX *ctx)
     339             : {
     340         240 :         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "dirsort",
     341             :                                 &vfs_dirsort_fns);
     342             : }

Generated by: LCOV version 1.13