2004-09-18 08:16:14 +00:00
/*
Unix SMB / CIFS implementation .
Copyright ( C ) Andrew Tridgell 2004
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
2007-07-10 02:07:03 +00:00
the Free Software Foundation ; either version 3 of the License , or
2004-09-18 08:16:14 +00:00
( 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
2007-07-10 02:07:03 +00:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2004-09-18 08:16:14 +00:00
*/
/*
directory listing functions for posix backend
*/
# include "includes.h"
# include "vfs_posix.h"
2004-11-02 00:24:21 +00:00
# include "system/dir.h"
2004-09-18 08:16:14 +00:00
2004-12-12 11:30:30 +00:00
# define NAME_CACHE_SIZE 100
struct name_cache_entry {
char * name ;
off_t offset ;
} ;
2004-10-26 13:18:34 +00:00
struct pvfs_dir {
struct pvfs_state * pvfs ;
2007-10-06 22:28:14 +00:00
bool no_wildcard ;
2004-12-12 11:30:30 +00:00
char * single_name ;
2004-10-26 22:25:34 +00:00
const char * pattern ;
2004-10-26 13:18:34 +00:00
off_t offset ;
DIR * dir ;
const char * unix_path ;
2007-10-06 22:28:14 +00:00
bool end_of_search ;
2004-12-12 11:30:30 +00:00
struct name_cache_entry * name_cache ;
uint32_t name_cache_index ;
2004-10-26 13:18:34 +00:00
} ;
2006-09-10 07:24:41 +00:00
/* these three numbers are chosen to minimise the chances of a bad
interaction with the OS value for ' end of directory ' . On IRIX
telldir ( ) returns 0xFFFFFFFF at the end of a directory , and that
caused an infinite loop with the original values of 0 , 1 , 2
2006-09-10 13:51:57 +00:00
On XFS on linux telldir returns 0x7FFFFFFF at the end of a
directory . Thus the change from 0x80000002 , as otherwise
0x7FFFFFFF + 0x80000002 = = 1 = = DIR_OFFSET_DOTDOT
2006-09-10 07:24:41 +00:00
*/
2005-07-25 10:40:17 +00:00
# define DIR_OFFSET_DOT 0
# define DIR_OFFSET_DOTDOT 1
2006-09-10 13:48:42 +00:00
# define DIR_OFFSET_BASE 0x80000022
2005-07-25 10:40:17 +00:00
2004-09-18 08:16:14 +00:00
/*
a special directory listing case where the pattern has no wildcard . We can just do a single stat ( )
thus avoiding the more expensive directory scan
*/
static NTSTATUS pvfs_list_no_wildcard ( struct pvfs_state * pvfs , struct pvfs_filename * name ,
const char * pattern , struct pvfs_dir * dir )
{
if ( ! name - > exists ) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
2004-10-26 13:18:34 +00:00
dir - > pvfs = pvfs ;
2007-10-06 22:28:14 +00:00
dir - > no_wildcard = true ;
dir - > end_of_search = false ;
2004-09-22 08:17:26 +00:00
dir - > unix_path = talloc_strdup ( dir , name - > full_name ) ;
if ( ! dir - > unix_path ) {
return NT_STATUS_NO_MEMORY ;
}
2004-09-18 08:16:14 +00:00
2004-12-12 11:30:30 +00:00
dir - > single_name = talloc_strdup ( dir , pattern ) ;
if ( ! dir - > single_name ) {
2004-09-18 08:16:14 +00:00
return NT_STATUS_NO_MEMORY ;
}
2004-10-26 13:18:34 +00:00
dir - > dir = NULL ;
dir - > offset = 0 ;
2004-10-26 22:25:34 +00:00
dir - > pattern = NULL ;
2004-09-18 08:16:14 +00:00
return NT_STATUS_OK ;
}
2004-10-26 13:18:34 +00:00
/*
destroy an open search
*/
2006-05-24 07:34:11 +00:00
static int pvfs_dirlist_destructor ( struct pvfs_dir * dir )
2004-10-26 13:18:34 +00:00
{
if ( dir - > dir ) closedir ( dir - > dir ) ;
return 0 ;
}
2004-09-18 08:16:14 +00:00
/*
2004-10-26 11:11:16 +00:00
start to read a directory
2004-09-18 08:16:14 +00:00
if the pattern matches no files then we return NT_STATUS_OK , with dir - > count = 0
*/
2004-10-26 13:18:34 +00:00
NTSTATUS pvfs_list_start ( struct pvfs_state * pvfs , struct pvfs_filename * name ,
TALLOC_CTX * mem_ctx , struct pvfs_dir * * dirp )
2004-09-18 08:16:14 +00:00
{
char * pattern ;
2004-10-26 13:18:34 +00:00
struct pvfs_dir * dir ;
2005-01-27 07:08:20 +00:00
( * dirp ) = talloc_zero ( mem_ctx , struct pvfs_dir ) ;
2004-10-26 13:18:34 +00:00
if ( * dirp = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
dir = * dirp ;
2004-09-18 08:16:14 +00:00
/* split the unix path into a directory + pattern */
pattern = strrchr ( name - > full_name , ' / ' ) ;
if ( ! pattern ) {
/* this should not happen, as pvfs_unix_path is supposed to
return an absolute path */
return NT_STATUS_UNSUCCESSFUL ;
}
* pattern + + = 0 ;
if ( ! name - > has_wildcard ) {
return pvfs_list_no_wildcard ( pvfs , name , pattern , dir ) ;
}
2004-09-27 08:39:51 +00:00
2004-09-22 08:17:26 +00:00
dir - > unix_path = talloc_strdup ( dir , name - > full_name ) ;
if ( ! dir - > unix_path ) {
return NT_STATUS_NO_MEMORY ;
}
2004-10-26 22:25:34 +00:00
dir - > pattern = talloc_strdup ( dir , pattern ) ;
if ( dir - > pattern = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2004-09-22 08:17:26 +00:00
2004-10-26 13:18:34 +00:00
dir - > dir = opendir ( name - > full_name ) ;
if ( ! dir - > dir ) {
2004-09-18 08:16:14 +00:00
return pvfs_map_errno ( pvfs , errno ) ;
}
2004-10-26 13:18:34 +00:00
dir - > pvfs = pvfs ;
2007-10-06 22:28:14 +00:00
dir - > no_wildcard = false ;
dir - > end_of_search = false ;
2006-09-10 07:24:41 +00:00
dir - > offset = DIR_OFFSET_DOT ;
2005-01-07 04:39:16 +00:00
dir - > name_cache = talloc_zero_array ( dir ,
struct name_cache_entry ,
NAME_CACHE_SIZE ) ;
2004-12-12 11:30:30 +00:00
if ( dir - > name_cache = = NULL ) {
talloc_free ( dir ) ;
return NT_STATUS_NO_MEMORY ;
}
2004-10-02 12:25:02 +00:00
2004-10-26 13:18:34 +00:00
talloc_set_destructor ( dir , pvfs_dirlist_destructor ) ;
2004-09-18 08:16:14 +00:00
2004-10-26 13:18:34 +00:00
return NT_STATUS_OK ;
}
2005-07-25 10:40:17 +00:00
/*
add an entry to the local cache
*/
static void dcache_add ( struct pvfs_dir * dir , const char * name )
{
struct name_cache_entry * e ;
dir - > name_cache_index = ( dir - > name_cache_index + 1 ) % NAME_CACHE_SIZE ;
e = & dir - > name_cache [ dir - > name_cache_index ] ;
if ( e - > name ) talloc_free ( e - > name ) ;
e - > name = talloc_strdup ( dir - > name_cache , name ) ;
e - > offset = dir - > offset ;
}
2004-10-26 13:18:34 +00:00
/*
return the next entry
*/
2006-09-10 07:24:41 +00:00
const char * pvfs_list_next ( struct pvfs_dir * dir , off_t * ofs )
2004-10-26 13:18:34 +00:00
{
struct dirent * de ;
2006-03-15 17:28:46 +00:00
enum protocol_types protocol = dir - > pvfs - > ntvfs - > ctx - > protocol ;
2004-10-26 13:18:34 +00:00
/* non-wildcard searches are easy */
if ( dir - > no_wildcard ) {
2007-10-06 22:28:14 +00:00
dir - > end_of_search = true ;
2004-10-26 13:18:34 +00:00
if ( * ofs ! = 0 ) return NULL ;
( * ofs ) + + ;
2004-12-12 11:30:30 +00:00
return dir - > single_name ;
2004-10-26 13:18:34 +00:00
}
2005-07-25 10:40:17 +00:00
/* . and .. are handled separately as some unix systems will
not return them first in a directory , but windows client
may assume that these entries always appear first */
if ( * ofs = = DIR_OFFSET_DOT ) {
2006-09-10 07:24:41 +00:00
( * ofs ) = DIR_OFFSET_DOTDOT ;
2004-10-26 13:18:34 +00:00
dir - > offset = * ofs ;
2005-07-25 10:40:17 +00:00
if ( ms_fnmatch ( dir - > pattern , " . " , protocol ) = = 0 ) {
dcache_add ( dir , " . " ) ;
return " . " ;
}
2004-10-26 13:18:34 +00:00
}
2005-07-25 10:40:17 +00:00
if ( * ofs = = DIR_OFFSET_DOTDOT ) {
2006-09-10 07:24:41 +00:00
( * ofs ) = DIR_OFFSET_BASE ;
2005-07-25 10:40:17 +00:00
dir - > offset = * ofs ;
if ( ms_fnmatch ( dir - > pattern , " .. " , protocol ) = = 0 ) {
dcache_add ( dir , " .. " ) ;
return " .. " ;
}
}
if ( * ofs = = DIR_OFFSET_BASE ) {
rewinddir ( dir - > dir ) ;
} else if ( * ofs ! = dir - > offset ) {
seekdir ( dir - > dir , ( * ofs ) - DIR_OFFSET_BASE ) ;
}
dir - > offset = * ofs ;
2004-10-26 13:18:34 +00:00
2004-10-26 22:25:34 +00:00
while ( ( de = readdir ( dir - > dir ) ) ) {
const char * dname = de - > d_name ;
2006-04-24 02:36:55 +00:00
if ( ISDOT ( dname ) | | ISDOTDOT ( dname ) ) {
2005-07-25 10:40:17 +00:00
continue ;
}
if ( ms_fnmatch ( dir - > pattern , dname , protocol ) ! = 0 ) {
2004-10-26 22:25:34 +00:00
char * short_name = pvfs_short_name_component ( dir - > pvfs , dname ) ;
if ( short_name = = NULL | |
2005-07-25 10:40:17 +00:00
ms_fnmatch ( dir - > pattern , short_name , protocol ) ! = 0 ) {
2004-10-26 22:25:34 +00:00
talloc_free ( short_name ) ;
continue ;
}
talloc_free ( short_name ) ;
}
2004-09-18 08:16:14 +00:00
2005-07-25 10:40:17 +00:00
dir - > offset = telldir ( dir - > dir ) + DIR_OFFSET_BASE ;
2004-10-26 22:25:34 +00:00
( * ofs ) = dir - > offset ;
2004-10-26 13:18:34 +00:00
2005-07-25 10:40:17 +00:00
dcache_add ( dir , dname ) ;
2004-12-12 11:30:30 +00:00
2005-07-25 10:40:17 +00:00
return dname ;
2004-10-26 22:25:34 +00:00
}
2007-10-06 22:28:14 +00:00
dir - > end_of_search = true ;
2004-10-26 22:25:34 +00:00
return NULL ;
2004-10-26 13:18:34 +00:00
}
2004-10-26 11:11:16 +00:00
/*
return unix directory of an open search
*/
const char * pvfs_list_unix_path ( struct pvfs_dir * dir )
{
return dir - > unix_path ;
}
/*
2007-10-06 22:28:14 +00:00
return true if end of search has been reached
2004-10-26 11:11:16 +00:00
*/
2007-10-06 22:28:14 +00:00
bool pvfs_list_eos ( struct pvfs_dir * dir , off_t ofs )
2004-10-26 11:11:16 +00:00
{
2004-10-26 13:18:34 +00:00
return dir - > end_of_search ;
2004-10-26 11:11:16 +00:00
}
/*
seek to the given name
*/
2006-09-10 07:24:41 +00:00
NTSTATUS pvfs_list_seek ( struct pvfs_dir * dir , const char * name , off_t * ofs )
2004-10-26 11:11:16 +00:00
{
2004-10-26 13:18:34 +00:00
struct dirent * de ;
2004-12-12 11:30:30 +00:00
int i ;
2004-10-26 13:18:34 +00:00
2007-10-06 22:28:14 +00:00
dir - > end_of_search = false ;
2006-09-10 07:24:41 +00:00
2006-04-24 00:16:51 +00:00
if ( ISDOT ( name ) ) {
2005-07-25 10:40:17 +00:00
dir - > offset = DIR_OFFSET_DOTDOT ;
* ofs = dir - > offset ;
return NT_STATUS_OK ;
}
2006-04-24 00:16:51 +00:00
if ( ISDOTDOT ( name ) ) {
2005-07-25 10:40:17 +00:00
dir - > offset = DIR_OFFSET_BASE ;
* ofs = dir - > offset ;
return NT_STATUS_OK ;
}
2004-12-12 11:30:30 +00:00
for ( i = dir - > name_cache_index ; i > = 0 ; i - - ) {
struct name_cache_entry * e = & dir - > name_cache [ i ] ;
2005-08-30 11:55:05 +00:00
if ( e - > name & & strcasecmp_m ( name , e - > name ) = = 0 ) {
2004-12-12 11:30:30 +00:00
* ofs = e - > offset ;
return NT_STATUS_OK ;
}
}
for ( i = NAME_CACHE_SIZE - 1 ; i > dir - > name_cache_index ; i - - ) {
struct name_cache_entry * e = & dir - > name_cache [ i ] ;
2005-08-30 11:55:05 +00:00
if ( e - > name & & strcasecmp_m ( name , e - > name ) = = 0 ) {
2004-12-12 11:30:30 +00:00
* ofs = e - > offset ;
return NT_STATUS_OK ;
}
2004-10-26 13:18:34 +00:00
}
rewinddir ( dir - > dir ) ;
while ( ( de = readdir ( dir - > dir ) ) ) {
2005-08-30 11:55:05 +00:00
if ( strcasecmp_m ( name , de - > d_name ) = = 0 ) {
2005-07-25 10:40:17 +00:00
dir - > offset = telldir ( dir - > dir ) + DIR_OFFSET_BASE ;
2004-10-26 13:18:34 +00:00
* ofs = dir - > offset ;
return NT_STATUS_OK ;
}
}
2007-10-06 22:28:14 +00:00
dir - > end_of_search = true ;
2004-10-26 13:18:34 +00:00
2004-12-12 11:30:30 +00:00
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
2004-10-26 11:11:16 +00:00
}
2005-08-03 17:22:33 +00:00
2006-09-10 07:24:41 +00:00
/*
seek to the given offset
*/
NTSTATUS pvfs_list_seek_ofs ( struct pvfs_dir * dir , uint32_t resume_key , off_t * ofs )
{
struct dirent * de ;
int i ;
2007-10-06 22:28:14 +00:00
dir - > end_of_search = false ;
2006-09-10 07:24:41 +00:00
if ( resume_key = = DIR_OFFSET_DOT ) {
* ofs = DIR_OFFSET_DOTDOT ;
return NT_STATUS_OK ;
}
if ( resume_key = = DIR_OFFSET_DOTDOT ) {
* ofs = DIR_OFFSET_BASE ;
return NT_STATUS_OK ;
}
if ( resume_key = = DIR_OFFSET_BASE ) {
rewinddir ( dir - > dir ) ;
if ( ( de = readdir ( dir - > dir ) ) = = NULL ) {
2007-10-06 22:28:14 +00:00
dir - > end_of_search = true ;
2006-09-10 07:24:41 +00:00
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
* ofs = telldir ( dir - > dir ) + DIR_OFFSET_BASE ;
dir - > offset = * ofs ;
return NT_STATUS_OK ;
}
for ( i = dir - > name_cache_index ; i > = 0 ; i - - ) {
struct name_cache_entry * e = & dir - > name_cache [ i ] ;
if ( resume_key = = ( uint32_t ) e - > offset ) {
* ofs = e - > offset ;
return NT_STATUS_OK ;
}
}
for ( i = NAME_CACHE_SIZE - 1 ; i > dir - > name_cache_index ; i - - ) {
struct name_cache_entry * e = & dir - > name_cache [ i ] ;
if ( resume_key = = ( uint32_t ) e - > offset ) {
* ofs = e - > offset ;
return NT_STATUS_OK ;
}
}
rewinddir ( dir - > dir ) ;
while ( ( de = readdir ( dir - > dir ) ) ) {
dir - > offset = telldir ( dir - > dir ) + DIR_OFFSET_BASE ;
if ( resume_key = = ( uint32_t ) dir - > offset ) {
* ofs = dir - > offset ;
return NT_STATUS_OK ;
}
}
2007-10-06 22:28:14 +00:00
dir - > end_of_search = true ;
2006-09-10 07:24:41 +00:00
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
2005-08-03 17:22:33 +00:00
/*
see if a directory is empty
*/
2007-10-06 22:28:14 +00:00
bool pvfs_directory_empty ( struct pvfs_state * pvfs , struct pvfs_filename * name )
2005-08-03 17:22:33 +00:00
{
struct dirent * de ;
DIR * dir = opendir ( name - > full_name ) ;
if ( dir = = NULL ) {
2007-10-06 22:28:14 +00:00
return true ;
2005-08-03 17:22:33 +00:00
}
while ( ( de = readdir ( dir ) ) ) {
2006-04-24 00:16:51 +00:00
if ( ! ISDOT ( de - > d_name ) & & ! ISDOTDOT ( de - > d_name ) ) {
2005-08-03 17:22:33 +00:00
closedir ( dir ) ;
2007-10-06 22:28:14 +00:00
return false ;
2005-08-03 17:22:33 +00:00
}
}
closedir ( dir ) ;
2007-10-06 22:28:14 +00:00
return true ;
2005-08-03 17:22:33 +00:00
}