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 23static int compare_dirent (const void *a, const void *b) { 24 const SMB_STRUCT_DIRENT *da = (const SMB_STRUCT_DIRENT *) a; 25 const SMB_STRUCT_DIRENT *db = (const SMB_STRUCT_DIRENT *) b; 26 return StrCaseCmp(da->d_name, db->d_name); 27} 28 29struct dirsort_privates { 30 long pos; 31 SMB_STRUCT_DIRENT *directory_list; 32 long number_of_entries; 33 time_t mtime; 34 SMB_STRUCT_DIR *source_directory; 35 int fd; 36}; 37 38static void free_dirsort_privates(void **datap) { 39 struct dirsort_privates *data = (struct dirsort_privates *) *datap; 40 SAFE_FREE(data->directory_list); 41 SAFE_FREE(data); 42 *datap = NULL; 43 44 return; 45} 46 47static void open_and_sort_dir (vfs_handle_struct *handle) 48{ 49 SMB_STRUCT_DIRENT *dp; 50 struct stat dir_stat; 51 long current_pos; 52 struct dirsort_privates *data = NULL; 53 54 SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates, return); 55 56 data->number_of_entries = 0; 57 58 if (fstat(data->fd, &dir_stat) == 0) { 59 data->mtime = dir_stat.st_mtime; 60 } 61 62 while (SMB_VFS_NEXT_READDIR(handle, data->source_directory, NULL) 63 != NULL) { 64 data->number_of_entries++; 65 } 66 67 /* Open the underlying directory and count the number of entries 68 Skip back to the beginning as we'll read it again */ 69 SMB_VFS_NEXT_REWINDDIR(handle, data->source_directory); 70 71 /* Set up an array and read the directory entries into it */ 72 SAFE_FREE(data->directory_list); /* destroy previous cache if needed */ 73 data->directory_list = (SMB_STRUCT_DIRENT *)SMB_MALLOC( 74 data->number_of_entries * sizeof(SMB_STRUCT_DIRENT)); 75 current_pos = data->pos; 76 data->pos = 0; 77 while ((dp = SMB_VFS_NEXT_READDIR(handle, data->source_directory, 78 NULL)) != NULL) { 79 data->directory_list[data->pos++] = *dp; 80 } 81 82 /* Sort the directory entries by name */ 83 data->pos = current_pos; 84 qsort(data->directory_list, data->number_of_entries, 85 sizeof(SMB_STRUCT_DIRENT), compare_dirent); 86} 87 88static SMB_STRUCT_DIR *dirsort_opendir(vfs_handle_struct *handle, 89 const char *fname, const char *mask, 90 uint32 attr) 91{ 92 struct dirsort_privates *data = NULL; 93 94 /* set up our private data about this directory */ 95 data = (struct dirsort_privates *)SMB_MALLOC( 96 sizeof(struct dirsort_privates)); 97 98 data->directory_list = NULL; 99 data->pos = 0; 100 101 /* Open the underlying directory and count the number of entries */ 102 data->source_directory = SMB_VFS_NEXT_OPENDIR(handle, fname, mask, 103 attr); 104 105 data->fd = dirfd(data->source_directory); 106 107 SMB_VFS_HANDLE_SET_DATA(handle, data, free_dirsort_privates, 108 struct dirsort_privates, return NULL); 109 110 open_and_sort_dir(handle); 111 112 return data->source_directory; 113} 114 115static SMB_STRUCT_DIRENT *dirsort_readdir(vfs_handle_struct *handle, 116 SMB_STRUCT_DIR *dirp, 117 SMB_STRUCT_STAT *sbuf) 118{ 119 struct dirsort_privates *data = NULL; 120 time_t current_mtime; 121 struct stat dir_stat; 122 123 SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates, 124 return NULL); 125 126 if (fstat(data->fd, &dir_stat) == -1) { 127 return NULL; 128 } 129 130 current_mtime = dir_stat.st_mtime; 131 132 /* throw away cache and re-read the directory if we've changed */ 133 if (current_mtime > data->mtime) { 134 open_and_sort_dir(handle); 135 } 136 137 if (data->pos >= data->number_of_entries) { 138 return NULL; 139 } 140 141 return &data->directory_list[data->pos++]; 142} 143 144static void dirsort_seekdir(vfs_handle_struct *handle, SMB_STRUCT_DIR *dirp, 145 long offset) 146{ 147 struct dirsort_privates *data = NULL; 148 SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates, return); 149 150 data->pos = offset; 151} 152 153static long dirsort_telldir(vfs_handle_struct *handle, SMB_STRUCT_DIR *dirp) 154{ 155 struct dirsort_privates *data = NULL; 156 SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates, 157 return -1); 158 159 return data->pos; 160} 161 162static void dirsort_rewinddir(vfs_handle_struct *handle, SMB_STRUCT_DIR *dirp) 163{ 164 struct dirsort_privates *data = NULL; 165 SMB_VFS_HANDLE_GET_DATA(handle, data, struct dirsort_privates, return); 166 167 data->pos = 0; 168} 169 170static struct vfs_fn_pointers vfs_dirsort_fns = { 171 .opendir = dirsort_opendir, 172 .readdir = dirsort_readdir, 173 .seekdir = dirsort_seekdir, 174 .telldir = dirsort_telldir, 175 .rewind_dir = dirsort_rewinddir, 176}; 177 178NTSTATUS vfs_dirsort_init(void) 179{ 180 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "dirsort", 181 &vfs_dirsort_fns); 182} 183