2009-03-22 13:06:52 +03:00
/*
* VFS module to provide a sorted directory list .
*
* Copyright ( C ) Andy Kelk ( andy @ mopoke . co . uk ) , 2009
*
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
2011-03-23 00:34:22 +03:00
# include "smbd/smbd.h"
2011-02-26 01:20:06 +03:00
# include "system/filesys.h"
2009-03-22 13:06:52 +03:00
2012-03-28 06:18:14 +04:00
static int compare_dirent ( const struct dirent * da , const struct dirent * db )
2010-02-14 02:01:17 +03:00
{
2011-05-13 22:21:30 +04:00
return strcasecmp_m ( da - > d_name , db - > d_name ) ;
2009-03-22 13:06:52 +03:00
}
struct dirsort_privates {
2014-01-30 05:01:30 +04:00
struct dirsort_privates * prev , * next ;
2009-03-22 13:06:52 +03:00
long pos ;
2012-03-28 06:18:14 +04:00
struct dirent * directory_list ;
2013-04-09 03:40:35 +04:00
unsigned int number_of_entries ;
2013-04-09 21:43:53 +04:00
struct timespec mtime ;
2012-03-28 06:22:03 +04:00
DIR * source_directory ;
2013-04-09 21:50:55 +04:00
files_struct * fsp ; /* If open via FDOPENDIR. */
struct smb_filename * smb_fname ; /* If open via OPENDIR */
2009-03-22 13:06:52 +03:00
} ;
2013-04-09 21:43:53 +04:00
static bool get_sorted_dir_mtime ( vfs_handle_struct * handle ,
struct dirsort_privates * data ,
struct timespec * ret_mtime )
{
int ret ;
2013-04-09 21:50:55 +04:00
struct timespec mtime ;
2019-09-09 08:57:34 +03:00
NTSTATUS status ;
2013-04-09 21:43:53 +04:00
2013-04-09 21:50:55 +04:00
if ( data - > fsp ) {
2019-09-09 08:57:34 +03:00
status = vfs_stat_fsp ( data - > fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return false ;
}
2013-04-09 21:50:55 +04:00
mtime = data - > fsp - > fsp_name - > st . st_ex_mtime ;
} else {
ret = SMB_VFS_STAT ( handle - > conn , data - > smb_fname ) ;
2019-09-09 08:57:34 +03:00
if ( ret = = - 1 ) {
return false ;
}
2013-04-09 21:50:55 +04:00
mtime = data - > smb_fname - > st . st_ex_mtime ;
}
2013-04-09 21:43:53 +04:00
2013-04-09 21:50:55 +04:00
* ret_mtime = mtime ;
2013-04-09 21:43:53 +04:00
return true ;
}
2013-04-09 21:29:47 +04:00
static bool open_and_sort_dir ( vfs_handle_struct * handle ,
struct dirsort_privates * data )
2009-03-22 13:06:52 +03:00
{
2017-02-09 17:05:01 +03:00
uint32_t total_count = 0 ;
/* This should be enough for most use cases */
uint32_t dirent_allocated = 64 ;
struct dirent * dp ;
2009-03-22 13:06:52 +03:00
data - > number_of_entries = 0 ;
2013-04-09 21:43:53 +04:00
if ( get_sorted_dir_mtime ( handle , data , & data - > mtime ) = = false ) {
return false ;
2009-03-22 13:06:52 +03:00
}
2020-11-22 15:57:27 +03:00
dp = SMB_VFS_NEXT_READDIR ( handle ,
data - > fsp ,
data - > source_directory ,
NULL ) ;
2017-02-09 17:05:01 +03:00
if ( dp = = NULL ) {
2013-04-09 03:31:53 +04:00
return false ;
}
2009-03-22 13:06:52 +03:00
/* Set up an array and read the directory entries into it */
2013-04-09 02:11:28 +04:00
TALLOC_FREE ( data - > directory_list ) ; /* destroy previous cache if needed */
data - > directory_list = talloc_zero_array ( data ,
2017-02-09 17:05:01 +03:00
struct dirent ,
dirent_allocated ) ;
if ( data - > directory_list = = NULL ) {
2010-09-10 22:56:26 +04:00
return false ;
}
2017-02-09 17:05:01 +03:00
do {
if ( total_count > = dirent_allocated ) {
struct dirent * dlist ;
/*
* Be memory friendly .
*
* We should not double the amount of memory . With a lot
* of files we reach easily 50 MB , and doubling will
* get much bigger just for a few files more .
*
* For 200 k files this means 50 memory reallocations .
*/
dirent_allocated + = 4096 ;
dlist = talloc_realloc ( data ,
data - > directory_list ,
struct dirent ,
dirent_allocated ) ;
if ( dlist = = NULL ) {
break ;
}
data - > directory_list = dlist ;
2013-04-09 03:40:35 +04:00
}
2017-02-09 17:05:01 +03:00
data - > directory_list [ total_count ] = * dp ;
total_count + + ;
2020-11-22 15:57:27 +03:00
dp = SMB_VFS_NEXT_READDIR ( handle ,
data - > fsp ,
data - > source_directory ,
NULL ) ;
2017-02-09 17:05:01 +03:00
} while ( dp ! = NULL ) ;
2009-03-22 13:06:52 +03:00
2017-02-09 17:05:01 +03:00
data - > number_of_entries = total_count ;
2013-04-09 03:40:35 +04:00
2009-03-22 13:06:52 +03:00
/* Sort the directory entries by name */
2010-02-14 02:01:17 +03:00
TYPESAFE_QSORT ( data - > directory_list , data - > number_of_entries , compare_dirent ) ;
2010-09-10 22:56:26 +04:00
return true ;
2009-03-22 13:06:52 +03:00
}
2012-03-28 06:22:03 +04:00
static DIR * dirsort_fdopendir ( vfs_handle_struct * handle ,
2011-02-09 02:07:48 +03:00
files_struct * fsp ,
const char * mask ,
2015-05-03 06:11:02 +03:00
uint32_t attr )
2011-02-09 02:07:48 +03:00
{
2014-01-30 05:01:30 +04:00
struct dirsort_privates * list_head = NULL ;
2011-02-09 02:07:48 +03:00
struct dirsort_privates * data = NULL ;
2014-01-30 05:01:30 +04:00
if ( SMB_VFS_HANDLE_TEST_DATA ( handle ) ) {
/* Find the list head of all open directories. */
SMB_VFS_HANDLE_GET_DATA ( handle , list_head , struct dirsort_privates ,
return NULL ) ;
}
2011-02-09 02:07:48 +03:00
/* set up our private data about this directory */
2013-04-09 02:11:28 +04:00
data = talloc_zero ( handle - > conn , struct dirsort_privates ) ;
2011-02-09 02:07:48 +03:00
if ( ! data ) {
return NULL ;
}
2013-04-09 21:50:55 +04:00
data - > fsp = fsp ;
2011-02-09 02:07:48 +03:00
/* Open the underlying directory and count the number of entries */
data - > source_directory = SMB_VFS_NEXT_FDOPENDIR ( handle , fsp , mask ,
attr ) ;
if ( data - > source_directory = = NULL ) {
2013-04-09 02:11:28 +04:00
TALLOC_FREE ( data ) ;
2011-02-09 02:07:48 +03:00
return NULL ;
}
2013-04-09 21:29:47 +04:00
if ( ! open_and_sort_dir ( handle , data ) ) {
2011-02-09 02:07:48 +03:00
SMB_VFS_NEXT_CLOSEDIR ( handle , data - > source_directory ) ;
2013-04-09 21:29:47 +04:00
TALLOC_FREE ( data ) ;
2011-02-09 02:07:48 +03:00
/* fd is now closed. */
2020-09-26 22:46:51 +03:00
fsp_set_fd ( fsp , - 1 ) ;
2011-02-09 02:07:48 +03:00
return NULL ;
}
2014-01-30 05:01:30 +04:00
/* Add to the private list of all open directories. */
DLIST_ADD ( list_head , data ) ;
SMB_VFS_HANDLE_SET_DATA ( handle , list_head , NULL ,
2013-04-09 21:29:47 +04:00
struct dirsort_privates , return NULL ) ;
2011-02-09 02:07:48 +03:00
return data - > source_directory ;
}
2012-03-28 06:18:14 +04:00
static struct dirent * dirsort_readdir ( vfs_handle_struct * handle ,
2020-11-22 15:57:27 +03:00
struct files_struct * dirfsp ,
DIR * dirp ,
SMB_STRUCT_STAT * sbuf )
2009-03-22 13:06:52 +03:00
{
struct dirsort_privates * data = NULL ;
2013-04-09 21:43:53 +04:00
struct timespec current_mtime ;
2009-03-22 13:06:52 +03:00
SMB_VFS_HANDLE_GET_DATA ( handle , data , struct dirsort_privates ,
return NULL ) ;
2014-01-30 05:01:30 +04:00
while ( data & & ( data - > source_directory ! = dirp ) ) {
data = data - > next ;
}
if ( data = = NULL ) {
return NULL ;
}
2013-04-09 21:43:53 +04:00
if ( get_sorted_dir_mtime ( handle , data , & current_mtime ) = = false ) {
2009-03-22 13:06:52 +03:00
return NULL ;
}
/* throw away cache and re-read the directory if we've changed */
2014-01-30 05:01:30 +04:00
if ( timespec_compare ( & current_mtime , & data - > mtime ) ) {
SMB_VFS_NEXT_REWINDDIR ( handle , data - > source_directory ) ;
2013-04-09 21:29:47 +04:00
open_and_sort_dir ( handle , data ) ;
2009-03-22 13:06:52 +03:00
}
if ( data - > pos > = data - > number_of_entries ) {
return NULL ;
}
return & data - > directory_list [ data - > pos + + ] ;
}
2012-03-28 06:22:03 +04:00
static void dirsort_rewinddir ( vfs_handle_struct * handle , DIR * dirp )
2009-03-22 13:06:52 +03:00
{
struct dirsort_privates * data = NULL ;
SMB_VFS_HANDLE_GET_DATA ( handle , data , struct dirsort_privates , return ) ;
2014-01-30 05:01:30 +04:00
/* Find the entry holding dirp. */
while ( data & & ( data - > source_directory ! = dirp ) ) {
data = data - > next ;
}
if ( data = = NULL ) {
return ;
}
2009-03-22 13:06:52 +03:00
data - > pos = 0 ;
}
2014-01-30 05:01:30 +04:00
static int dirsort_closedir ( vfs_handle_struct * handle , DIR * dirp )
{
struct dirsort_privates * list_head = NULL ;
struct dirsort_privates * data = NULL ;
int ret ;
SMB_VFS_HANDLE_GET_DATA ( handle , list_head , struct dirsort_privates , return - 1 ) ;
/* Find the entry holding dirp. */
for ( data = list_head ; data & & ( data - > source_directory ! = dirp ) ; data = data - > next ) {
;
}
if ( data = = NULL ) {
return - 1 ;
}
/* Remove from the list and re-store the list head. */
DLIST_REMOVE ( list_head , data ) ;
SMB_VFS_HANDLE_SET_DATA ( handle , list_head , NULL ,
struct dirsort_privates , return - 1 ) ;
ret = SMB_VFS_NEXT_CLOSEDIR ( handle , dirp ) ;
TALLOC_FREE ( data ) ;
return ret ;
}
2009-07-24 04:28:58 +04:00
static struct vfs_fn_pointers vfs_dirsort_fns = {
2011-12-04 08:45:04 +04:00
. fdopendir_fn = dirsort_fdopendir ,
. readdir_fn = dirsort_readdir ,
. rewind_dir_fn = dirsort_rewinddir ,
2014-01-30 05:01:30 +04:00
. closedir_fn = dirsort_closedir ,
2009-03-22 13:06:52 +03:00
} ;
2015-08-13 19:16:20 +03:00
static_decl_vfs ;
2017-04-20 22:24:43 +03:00
NTSTATUS vfs_dirsort_init ( TALLOC_CTX * ctx )
2009-03-22 13:06:52 +03:00
{
return smb_register_vfs ( SMB_VFS_INTERFACE_VERSION , " dirsort " ,
2009-07-24 04:28:58 +04:00
& vfs_dirsort_fns ) ;
2009-03-22 13:06:52 +03:00
}