LCOV - code coverage report
Current view: top level - source3/modules - vfs_dirsort.c (source / functions) Hit Total Coverage
Test: coverage report for master 2b515b7d Lines: 78 98 79.6 %
Date: 2024-02-28 12:06:22 Functions: 9 9 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    12306910 : static int compare_dirent (const struct dirent *da, const struct dirent *db)
      26             : {
      27    12306910 :         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     1687486 : 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     1687486 :         if (data->fsp) {
      50     1687486 :                 status = vfs_stat_fsp(data->fsp);
      51     1687486 :                 if (!NT_STATUS_IS_OK(status)) {
      52           0 :                         return false;
      53             :                 }
      54     1687486 :                 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     1687486 :         *ret_mtime = mtime;
      64             : 
      65     1687486 :         return true;
      66             : }
      67             : 
      68        5291 : static bool open_and_sort_dir(vfs_handle_struct *handle,
      69             :                                 struct dirsort_privates *data)
      70             : {
      71        5291 :         uint32_t total_count = 0;
      72             :         /* This should be enough for most use cases */
      73        5291 :         uint32_t dirent_allocated = 64;
      74             :         struct dirent *dp;
      75             : 
      76        5291 :         data->number_of_entries = 0;
      77             : 
      78        5291 :         if (get_sorted_dir_mtime(handle, data, &data->mtime) == false) {
      79           0 :                 return false;
      80             :         }
      81             : 
      82        5291 :         dp = SMB_VFS_NEXT_READDIR(handle, data->fsp, data->source_directory);
      83        5291 :         if (dp == NULL) {
      84           0 :                 return false;
      85             :         }
      86             : 
      87             :         /* Set up an array and read the directory entries into it */
      88        5291 :         TALLOC_FREE(data->directory_list); /* destroy previous cache if needed */
      89        5291 :         data->directory_list = talloc_zero_array(data,
      90             :                                                  struct dirent,
      91             :                                                  dirent_allocated);
      92        5291 :         if (data->directory_list == NULL) {
      93           0 :                 return false;
      94             :         }
      95             : 
      96             :         do {
      97     1647482 :                 if (total_count >= dirent_allocated) {
      98             :                         struct dirent *dlist;
      99             : 
     100             :                         /*
     101             :                          * Be memory friendly.
     102             :                          *
     103             :                          * We should not double the amount of memory. With a lot
     104             :                          * of files we reach easily 50MB, and doubling will
     105             :                          * get much bigger just for a few files more.
     106             :                          *
     107             :                          * For 200k files this means 50 memory reallocations.
     108             :                          */
     109        4362 :                         dirent_allocated += 4096;
     110             : 
     111        4362 :                         dlist = talloc_realloc(data,
     112             :                                                data->directory_list,
     113             :                                                struct dirent,
     114             :                                                dirent_allocated);
     115        4362 :                         if (dlist == NULL) {
     116           0 :                                 break;
     117             :                         }
     118        4362 :                         data->directory_list = dlist;
     119             :                 }
     120     1647482 :                 data->directory_list[total_count] = *dp;
     121             : 
     122     1647482 :                 total_count++;
     123     1647482 :                 dp = SMB_VFS_NEXT_READDIR(handle,
     124             :                                           data->fsp,
     125             :                                           data->source_directory);
     126     1647482 :         } while (dp != NULL);
     127             : 
     128        5291 :         data->number_of_entries = total_count;
     129             : 
     130             :         /* Sort the directory entries by name */
     131        5291 :         TYPESAFE_QSORT(data->directory_list, data->number_of_entries, compare_dirent);
     132        5291 :         return true;
     133             : }
     134             : 
     135        4939 : static DIR *dirsort_fdopendir(vfs_handle_struct *handle,
     136             :                                         files_struct *fsp,
     137             :                                         const char *mask,
     138             :                                         uint32_t attr)
     139             : {
     140        4939 :         struct dirsort_privates *list_head = NULL;
     141        4939 :         struct dirsort_privates *data = NULL;
     142             : 
     143        4939 :         if (SMB_VFS_HANDLE_TEST_DATA(handle)) {
     144             :                 /* Find the list head of all open directories. */
     145         112 :                 SMB_VFS_HANDLE_GET_DATA(handle, list_head, struct dirsort_privates,
     146             :                                 return NULL);
     147             :         }
     148             : 
     149             :         /* set up our private data about this directory */
     150        4939 :         data = talloc_zero(handle->conn, struct dirsort_privates);
     151        4939 :         if (!data) {
     152           0 :                 return NULL;
     153             :         }
     154             : 
     155        4939 :         data->fsp = fsp;
     156             : 
     157             :         /* Open the underlying directory and count the number of entries */
     158        4939 :         data->source_directory = SMB_VFS_NEXT_FDOPENDIR(handle, fsp, mask,
     159             :                                                       attr);
     160             : 
     161        4939 :         if (data->source_directory == NULL) {
     162           0 :                 TALLOC_FREE(data);
     163           0 :                 return NULL;
     164             :         }
     165             : 
     166        4939 :         if (!open_and_sort_dir(handle, data)) {
     167           0 :                 SMB_VFS_NEXT_CLOSEDIR(handle,data->source_directory);
     168           0 :                 TALLOC_FREE(data);
     169             :                 /* fd is now closed. */
     170           0 :                 fsp_set_fd(fsp, -1);
     171           0 :                 return NULL;
     172             :         }
     173             : 
     174             :         /* Add to the private list of all open directories. */
     175        4939 :         DLIST_ADD(list_head, data);
     176        4939 :         SMB_VFS_HANDLE_SET_DATA(handle, list_head, NULL,
     177             :                                 struct dirsort_privates, return NULL);
     178             : 
     179        4939 :         return data->source_directory;
     180             : }
     181             : 
     182     1682195 : static struct dirent *dirsort_readdir(vfs_handle_struct *handle,
     183             :                                       struct files_struct *dirfsp,
     184             :                                       DIR *dirp)
     185             : {
     186     1682195 :         struct dirsort_privates *data = NULL;
     187             :         struct timespec current_mtime;
     188             : 
     189     1682195 :         SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates,
     190             :                                 return NULL);
     191             : 
     192     1684095 :         while(data && (data->source_directory != dirp)) {
     193        1900 :                 data = data->next;
     194             :         }
     195     1682195 :         if (data == NULL) {
     196           0 :                 return NULL;
     197             :         }
     198             : 
     199     1682195 :         if (get_sorted_dir_mtime(handle, data, &current_mtime) == false) {
     200           0 :                 return NULL;
     201             :         }
     202             : 
     203             :         /* throw away cache and re-read the directory if we've changed */
     204     1682195 :         if (timespec_compare(&current_mtime, &data->mtime)) {
     205         352 :                 SMB_VFS_NEXT_REWINDDIR(handle, data->source_directory);
     206         352 :                 open_and_sort_dir(handle, data);
     207             :         }
     208             : 
     209     1682195 :         if (data->pos >= data->number_of_entries) {
     210        5247 :                 return NULL;
     211             :         }
     212             : 
     213     1676948 :         return &data->directory_list[data->pos++];
     214             : }
     215             : 
     216         738 : static void dirsort_rewinddir(vfs_handle_struct *handle, DIR *dirp)
     217             : {
     218         738 :         struct dirsort_privates *data = NULL;
     219         738 :         SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates, return);
     220             : 
     221             :         /* Find the entry holding dirp. */
     222        1118 :         while(data && (data->source_directory != dirp)) {
     223         380 :                 data = data->next;
     224             :         }
     225         738 :         if (data == NULL) {
     226           0 :                 return;
     227             :         }
     228         738 :         data->pos = 0;
     229             : }
     230             : 
     231        4939 : static int dirsort_closedir(vfs_handle_struct *handle, DIR *dirp)
     232             : {
     233        4939 :         struct dirsort_privates *list_head = NULL;
     234        4939 :         struct dirsort_privates *data = NULL;
     235             :         int ret;
     236             : 
     237        4939 :         SMB_VFS_HANDLE_GET_DATA(handle, list_head, struct dirsort_privates, return -1);
     238             :         /* Find the entry holding dirp. */
     239        4939 :         for(data = list_head; data && (data->source_directory != dirp); data = data->next) {
     240             :                 ;
     241             :         }
     242        4939 :         if (data == NULL) {
     243           0 :                 return -1;
     244             :         }
     245             :         /* Remove from the list and re-store the list head. */
     246        4939 :         DLIST_REMOVE(list_head, data);
     247        4939 :         SMB_VFS_HANDLE_SET_DATA(handle, list_head, NULL,
     248             :                                 struct dirsort_privates, return -1);
     249             : 
     250        4939 :         ret = SMB_VFS_NEXT_CLOSEDIR(handle, dirp);
     251        4939 :         TALLOC_FREE(data);
     252        4939 :         return ret;
     253             : }
     254             : 
     255             : static struct vfs_fn_pointers vfs_dirsort_fns = {
     256             :         .fdopendir_fn = dirsort_fdopendir,
     257             :         .readdir_fn = dirsort_readdir,
     258             :         .rewind_dir_fn = dirsort_rewinddir,
     259             :         .closedir_fn = dirsort_closedir,
     260             : };
     261             : 
     262             : static_decl_vfs;
     263         256 : NTSTATUS vfs_dirsort_init(TALLOC_CTX *ctx)
     264             : {
     265         256 :         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "dirsort",
     266             :                                 &vfs_dirsort_fns);
     267             : }

Generated by: LCOV version 1.14