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
2010-02-14 02:01:17 +03:00
static int compare_dirent ( const SMB_STRUCT_DIRENT * da , const SMB_STRUCT_DIRENT * db )
{
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 {
long pos ;
SMB_STRUCT_DIRENT * directory_list ;
long number_of_entries ;
time_t mtime ;
SMB_STRUCT_DIR * source_directory ;
int fd ;
} ;
static void free_dirsort_privates ( void * * datap ) {
struct dirsort_privates * data = ( struct dirsort_privates * ) * datap ;
SAFE_FREE ( data - > directory_list ) ;
SAFE_FREE ( data ) ;
* datap = NULL ;
return ;
}
2010-09-10 22:56:26 +04:00
static bool open_and_sort_dir ( vfs_handle_struct * handle )
2009-03-22 13:06:52 +03:00
{
SMB_STRUCT_DIRENT * dp ;
struct stat dir_stat ;
long current_pos ;
struct dirsort_privates * data = NULL ;
2010-09-10 22:56:26 +04:00
SMB_VFS_HANDLE_GET_DATA ( handle , data , struct dirsort_privates ,
return false ) ;
2009-03-22 13:06:52 +03:00
data - > number_of_entries = 0 ;
if ( fstat ( data - > fd , & dir_stat ) = = 0 ) {
data - > mtime = dir_stat . st_mtime ;
}
while ( SMB_VFS_NEXT_READDIR ( handle , data - > source_directory , NULL )
! = NULL ) {
data - > number_of_entries + + ;
}
/* Open the underlying directory and count the number of entries
Skip back to the beginning as we ' ll read it again */
SMB_VFS_NEXT_REWINDDIR ( handle , data - > source_directory ) ;
/* Set up an array and read the directory entries into it */
SAFE_FREE ( data - > directory_list ) ; /* destroy previous cache if needed */
data - > directory_list = ( SMB_STRUCT_DIRENT * ) SMB_MALLOC (
data - > number_of_entries * sizeof ( SMB_STRUCT_DIRENT ) ) ;
2010-09-10 22:56:26 +04:00
if ( ! data - > directory_list ) {
return false ;
}
2009-03-22 13:06:52 +03:00
current_pos = data - > pos ;
data - > pos = 0 ;
while ( ( dp = SMB_VFS_NEXT_READDIR ( handle , data - > source_directory ,
NULL ) ) ! = NULL ) {
data - > directory_list [ data - > pos + + ] = * dp ;
}
/* Sort the directory entries by name */
data - > pos = current_pos ;
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
}
static SMB_STRUCT_DIR * dirsort_opendir ( vfs_handle_struct * handle ,
const char * fname , const char * mask ,
uint32 attr )
{
struct dirsort_privates * data = NULL ;
/* set up our private data about this directory */
data = ( struct dirsort_privates * ) SMB_MALLOC (
sizeof ( struct dirsort_privates ) ) ;
2010-09-10 22:56:26 +04:00
if ( ! data ) {
return NULL ;
}
2009-03-22 13:06:52 +03:00
data - > directory_list = NULL ;
data - > pos = 0 ;
/* Open the underlying directory and count the number of entries */
data - > source_directory = SMB_VFS_NEXT_OPENDIR ( handle , fname , mask ,
attr ) ;
data - > fd = dirfd ( data - > source_directory ) ;
SMB_VFS_HANDLE_SET_DATA ( handle , data , free_dirsort_privates ,
struct dirsort_privates , return NULL ) ;
2010-09-10 22:56:26 +04:00
if ( ! open_and_sort_dir ( handle ) ) {
SMB_VFS_NEXT_CLOSEDIR ( handle , data - > source_directory ) ;
return NULL ;
}
2009-03-22 13:06:52 +03:00
return data - > source_directory ;
}
2011-02-09 02:07:48 +03:00
static SMB_STRUCT_DIR * dirsort_fdopendir ( vfs_handle_struct * handle ,
files_struct * fsp ,
const char * mask ,
uint32 attr )
{
struct dirsort_privates * data = NULL ;
/* set up our private data about this directory */
data = ( struct dirsort_privates * ) SMB_MALLOC (
sizeof ( struct dirsort_privates ) ) ;
if ( ! data ) {
return NULL ;
}
data - > directory_list = NULL ;
data - > pos = 0 ;
/* 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 ) {
SAFE_FREE ( data ) ;
return NULL ;
}
data - > fd = dirfd ( data - > source_directory ) ;
SMB_VFS_HANDLE_SET_DATA ( handle , data , free_dirsort_privates ,
struct dirsort_privates , return NULL ) ;
if ( ! open_and_sort_dir ( handle ) ) {
SMB_VFS_NEXT_CLOSEDIR ( handle , data - > source_directory ) ;
/* fd is now closed. */
fsp - > fh - > fd = - 1 ;
return NULL ;
}
return data - > source_directory ;
}
2009-03-22 13:06:52 +03:00
static SMB_STRUCT_DIRENT * dirsort_readdir ( vfs_handle_struct * handle ,
2009-07-19 04:32:44 +04:00
SMB_STRUCT_DIR * dirp ,
SMB_STRUCT_STAT * sbuf )
2009-03-22 13:06:52 +03:00
{
struct dirsort_privates * data = NULL ;
time_t current_mtime ;
struct stat dir_stat ;
SMB_VFS_HANDLE_GET_DATA ( handle , data , struct dirsort_privates ,
return NULL ) ;
if ( fstat ( data - > fd , & dir_stat ) = = - 1 ) {
return NULL ;
}
current_mtime = dir_stat . st_mtime ;
/* throw away cache and re-read the directory if we've changed */
if ( current_mtime > data - > mtime ) {
open_and_sort_dir ( handle ) ;
}
if ( data - > pos > = data - > number_of_entries ) {
return NULL ;
}
return & data - > directory_list [ data - > pos + + ] ;
}
static void dirsort_seekdir ( vfs_handle_struct * handle , SMB_STRUCT_DIR * dirp ,
long offset )
{
struct dirsort_privates * data = NULL ;
SMB_VFS_HANDLE_GET_DATA ( handle , data , struct dirsort_privates , return ) ;
data - > pos = offset ;
}
static long dirsort_telldir ( vfs_handle_struct * handle , SMB_STRUCT_DIR * dirp )
{
struct dirsort_privates * data = NULL ;
SMB_VFS_HANDLE_GET_DATA ( handle , data , struct dirsort_privates ,
return - 1 ) ;
return data - > pos ;
}
static void dirsort_rewinddir ( vfs_handle_struct * handle , SMB_STRUCT_DIR * dirp )
{
struct dirsort_privates * data = NULL ;
SMB_VFS_HANDLE_GET_DATA ( handle , data , struct dirsort_privates , return ) ;
data - > pos = 0 ;
}
2009-07-24 04:28:58 +04:00
static struct vfs_fn_pointers vfs_dirsort_fns = {
2011-12-04 08:45:04 +04:00
. opendir_fn = dirsort_opendir ,
. fdopendir_fn = dirsort_fdopendir ,
. readdir_fn = dirsort_readdir ,
. seekdir_fn = dirsort_seekdir ,
. telldir_fn = dirsort_telldir ,
. rewind_dir_fn = dirsort_rewinddir ,
2009-03-22 13:06:52 +03:00
} ;
NTSTATUS vfs_dirsort_init ( void )
{
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
}