- fix bug (wasn't setting mount_point of dir being mounted => crash while writing screenshot)
- add check for changing filters while enumerating dirents - add path_component_valid - change iterator to only return valid entries (removing that burden from user code) This was SVN commit r2067.
This commit is contained in:
parent
f777979cc8
commit
2fa430a4e6
@ -355,7 +355,7 @@ static int dirent_cb(const char* name, const struct stat* s, uintptr_t user)
|
||||
|
||||
char new_path[VFS_MAX_PATH];
|
||||
CHECK_ERR(path_append(new_path, p_path, name));
|
||||
TDir* new_dir = tree_add_dir(dir, new_path, mount_point);
|
||||
TDir* new_dir = tree_add_dir(dir, name);
|
||||
dir_queue->push_back(DirAndPath(new_dir, new_path));
|
||||
return 0;
|
||||
}
|
||||
@ -396,13 +396,18 @@ static int dirent_cb(const char* name, const struct stat* s, uintptr_t user)
|
||||
static int populate_dir(TDir* dir_, const char* p_path_, const TMountPoint* mount_point, bool recursive, TMountPoints* archives)
|
||||
{
|
||||
DirQueue dir_queue;
|
||||
dir_queue.push_back(DirAndPath(dir_, p_path_));
|
||||
DirQueue* const pdir_queue = recursive? &dir_queue : 0;
|
||||
|
||||
// kickoff (less efficient than goto, but c_str reference requires
|
||||
// pop to come at end of loop => this is easiest)
|
||||
dir_queue.push_back(DirAndPath(dir_, p_path_));
|
||||
|
||||
do
|
||||
{
|
||||
TDir* dir = dir_queue.front().dir;
|
||||
TDir* const dir = dir_queue.front().dir;
|
||||
const char* p_path = dir_queue.front().path.c_str();
|
||||
|
||||
tree_mount(dir, p_path, mount_point);
|
||||
|
||||
// add files and subdirs to this dir;
|
||||
// also adds the contents of archives if archives != 0.
|
||||
@ -493,8 +498,8 @@ static int remount(Mount& m)
|
||||
const char* p_real_path = m.p_real_path.c_str();
|
||||
const int flags = m.flags;
|
||||
const uint pri = m.pri;
|
||||
TMountPoint* mount_point = &m.mount_point;
|
||||
TMountPoints& archives = m.archives;
|
||||
TMountPoint* mount_point = &m.mount_point;
|
||||
TMountPoints& archives = m.archives;
|
||||
|
||||
// callers have a tendency to forget required trailing '/';
|
||||
// complain if it's not there, unless path = "" (root dir).
|
||||
@ -528,10 +533,14 @@ static int unmount(Mount& m)
|
||||
|
||||
// trivial, but used by vfs_shutdown and vfs_rebuild
|
||||
static inline void unmount_all(void)
|
||||
{ std::for_each(mounts.begin(), mounts.end(), unmount); }
|
||||
{
|
||||
std::for_each(mounts.begin(), mounts.end(), unmount);
|
||||
}
|
||||
|
||||
static inline void remount_all()
|
||||
{ std::for_each(mounts.begin(), mounts.end(), remount); }
|
||||
{
|
||||
std::for_each(mounts.begin(), mounts.end(), remount);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -568,14 +577,9 @@ int vfs_mount(const char* v_mount_point, const char* p_real_path, int flags, uin
|
||||
return -1;
|
||||
}
|
||||
|
||||
CHECK_PATH(v_mount_point);
|
||||
|
||||
const Mount& new_mount = Mount(v_mount_point, p_real_path, flags, pri);
|
||||
mounts.push_back(new_mount);
|
||||
|
||||
// actually mount the entry
|
||||
Mount& m = mounts.back();
|
||||
return remount(m);
|
||||
mounts.push_back(Mount(v_mount_point, p_real_path, flags, pri));
|
||||
return remount(mounts.back());
|
||||
}
|
||||
|
||||
|
||||
@ -653,12 +657,18 @@ static int make_file_path(char* path, const char* vfs_path, const TMountPoint* m
|
||||
|
||||
struct VDir
|
||||
{
|
||||
// we need to cache the complete contents of the directory:
|
||||
// xxx we need to cache the complete contents of the directory:
|
||||
// if we reference the real directory and it changes,
|
||||
// the c_str pointers may become invalid, and some files
|
||||
// may be returned out of order / not at all.
|
||||
// we copy the directory's subdirectory and file containers.
|
||||
void* latch;
|
||||
|
||||
// safety check
|
||||
#ifndef NDEBUG
|
||||
const char* filter;
|
||||
bool filter_latched;
|
||||
#endif
|
||||
};
|
||||
|
||||
H_TYPE_DEFINE(VDir);
|
||||
@ -710,31 +720,44 @@ int vfs_close_dir(Handle& hd)
|
||||
return h_free(hd, H_VDir);
|
||||
}
|
||||
|
||||
// make sure we can assign directly from TDirent to vfsDirEnt
|
||||
// (more efficient)
|
||||
cassert(offsetof(vfsDirEnt, name) == offsetof(TDirent, name));
|
||||
cassert(offsetof(vfsDirEnt, size) == offsetof(TDirent, size));
|
||||
cassert(offsetof(vfsDirEnt, mtime) == offsetof(TDirent, mtime));
|
||||
|
||||
// retrieve the next dir entry (in alphabetical order) matching <filter>.
|
||||
// return 0 on success, ERR_DIR_END if no matching entry was found,
|
||||
// or a negative error code on failure.
|
||||
// filter values:
|
||||
// - 0: any file;
|
||||
// - "/": any subdirectory
|
||||
// - anything else: pattern for name (may include '?' and '*' wildcards)
|
||||
// - 0: anything;
|
||||
// - "/": any subdirectory;
|
||||
// - "/|<pattern>": any subdirectory, or as below with <pattern>;
|
||||
// - <pattern>: any file whose name matches; ? and * wildcards are allowed.
|
||||
//
|
||||
// xxx rationale: the filename is currently stored internally as
|
||||
// std::string (=> less manual memory allocation). we don't want to
|
||||
// return a reference, because that would break C compatibility.
|
||||
// we're trying to avoid fixed-size buffers, so that is out as well.
|
||||
// finally, allocating a copy is not so good because it has to be
|
||||
// freed by the user (won't happen). returning a volatile pointer
|
||||
// to the string itself via c_str is the only remaining option.
|
||||
// note that the directory entries are only scanned once; after the
|
||||
// end is reached (-> ERR_DIR_END returned), no further entries can
|
||||
// be retrieved, even if filter changes (which shouldn't happen - see impl).
|
||||
//
|
||||
// rationale for returning a pointer to the name string: we're trying to
|
||||
// avoid arbitrary name length limits, so fixed-size buffers are out.
|
||||
// allocating a copy isn't good because it has to be freed by the user
|
||||
// (won't happen). that leaves a (const!) pointer to the internal storage.
|
||||
int vfs_next_dirent(const Handle hd, vfsDirEnt* ent, const char* filter)
|
||||
{
|
||||
H_DEREF(hd, VDir, vd);
|
||||
return tree_next_dirent(vd->latch, filter, (TDirent*)ent);
|
||||
|
||||
// warn if scanning the directory twice with different filters
|
||||
// (this used to work with dir/file because they were stored separately).
|
||||
// it is imaginable that someone will want to change it, but until
|
||||
// there's a good reason, leave this check in. note: only comparing
|
||||
// pointers isn't 100% certain, but it's safe enough and easy.
|
||||
#ifndef NDEBUG
|
||||
if(!vd->filter_latched)
|
||||
{
|
||||
vd->filter = filter;
|
||||
vd->filter_latched = true;
|
||||
}
|
||||
if(vd->filter != filter)
|
||||
debug_warn("vfs_next_dirent: filter has changed for this directory. are you scanning it twice?");
|
||||
#endif
|
||||
|
||||
return tree_next_dirent(vd->latch, filter, ent);
|
||||
}
|
||||
|
||||
|
||||
|
@ -118,8 +118,18 @@ extern int vfs_close_dir(Handle& hd);
|
||||
// or a negative error code on failure.
|
||||
// filter values:
|
||||
// - 0: anything;
|
||||
// - "/": any subdirectory
|
||||
// - anything else: pattern for name (may include '?' and '*' wildcards)
|
||||
// - "/": any subdirectory;
|
||||
// - "/|<pattern>": any subdirectory, or as below with <pattern>;
|
||||
// - <pattern>: any file whose name matches; ? and * wildcards are allowed.
|
||||
//
|
||||
// note that the directory entries are only scanned once; after the
|
||||
// end is reached (-> ERR_DIR_END returned), no further entries can
|
||||
// be retrieved, even if filter changes (which shouldn't happen - see impl).
|
||||
//
|
||||
// rationale for returning a pointer to the name string: we're trying to
|
||||
// avoid arbitrary name length limits, so fixed-size buffers are out.
|
||||
// allocating a copy isn't good because it has to be freed by the user
|
||||
// (won't happen). that leaves a (const!) pointer to the internal storage.
|
||||
extern int vfs_next_dirent(Handle hd, vfsDirEnt* ent, const char* filter = 0);
|
||||
|
||||
|
||||
|
@ -92,6 +92,32 @@ ok:
|
||||
#define CHECK_PATH(path) CHECK_ERR(path_validate(__LINE__, path))
|
||||
|
||||
|
||||
bool path_component_valid(const char* name)
|
||||
{
|
||||
// disallow empty strings
|
||||
if(*name == '\0')
|
||||
return false;
|
||||
|
||||
int c = 0;
|
||||
for(;;)
|
||||
{
|
||||
int last_c = c;
|
||||
c = *name++;
|
||||
|
||||
// disallow '..' (would allow escaping the VFS root dir sandbox)
|
||||
if(c == '.' && last_c == '.')
|
||||
return false;
|
||||
|
||||
// disallow dir separators
|
||||
if(c == '\\' || c == ':' || c == '/')
|
||||
return false;
|
||||
|
||||
if(c == '\0')
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// convenience function
|
||||
inline void path_copy(char* dst, const char* src)
|
||||
{
|
||||
@ -129,6 +155,7 @@ int path_append(char* dst, const char* path1, const char* path2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// strip <remove> from the start of <src>, prepend <replace>,
|
||||
// and write to <dst>.
|
||||
// used when converting VFS <--> real paths.
|
||||
|
@ -10,6 +10,8 @@ extern int path_validate(const uint line, const char* path);
|
||||
|
||||
#define CHECK_PATH(path) CHECK_ERR(path_validate(__LINE__, path))
|
||||
|
||||
extern bool path_component_valid(const char* name);
|
||||
|
||||
// convenience function
|
||||
extern void path_copy(char* dst, const char* src);
|
||||
|
||||
|
@ -54,7 +54,10 @@ public:
|
||||
TNode* add(const char* fn);
|
||||
TNode* find(const char* fn);
|
||||
|
||||
size_t size() { return num_entries; }
|
||||
size_t size()
|
||||
{
|
||||
return num_entries;
|
||||
}
|
||||
|
||||
class iterator;
|
||||
iterator begin() const;
|
||||
@ -88,7 +91,7 @@ public:
|
||||
void init();
|
||||
TNode* find(const char* fn, TNodeType desired_type);
|
||||
TNode* add(const char* fn, TNodeType new_type);
|
||||
TDir* add_dir(const char* path, const TMountPoint* mount_point_);
|
||||
int mount(const char* path, const TMountPoint*, bool watch);
|
||||
int lookup(const char* path, uint flags, TNode** pnode, char* exact_path);
|
||||
void clearR();
|
||||
void displayR(int indent_level);
|
||||
@ -187,42 +190,70 @@ void node_free_all()
|
||||
class TChildren::iterator
|
||||
{
|
||||
public:
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef TNode* T;
|
||||
typedef T value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef const TNode** pointer;
|
||||
typedef const TNode*& reference;
|
||||
typedef const T* pointer;
|
||||
typedef const T& reference;
|
||||
|
||||
iterator()
|
||||
{}
|
||||
iterator(T* pos_) : pos(pos_)
|
||||
{}
|
||||
T& operator[](int idx) const
|
||||
{ return pos[idx]; }
|
||||
{
|
||||
}
|
||||
iterator(T* pos_, T* end_) : pos(pos_), end(end_)
|
||||
{
|
||||
}
|
||||
T& operator*() const
|
||||
{ return *pos; }
|
||||
const T* operator->() const
|
||||
{ return &**this; }
|
||||
{
|
||||
return *pos;
|
||||
}
|
||||
iterator& operator++() // pre
|
||||
{ ++pos; return (*this); }
|
||||
iterator operator++(int) // post
|
||||
{ iterator tmp = *this; ++*this; return tmp; }
|
||||
{
|
||||
do
|
||||
pos++;
|
||||
while(pos != end && *pos == 0);
|
||||
return (*this);
|
||||
}
|
||||
bool operator==(const iterator& rhs) const
|
||||
{ return pos == rhs.pos; }
|
||||
bool operator!=(const iterator& rhs) const
|
||||
{ return !(*this == rhs); }
|
||||
{
|
||||
return pos == rhs.pos;
|
||||
}
|
||||
bool operator<(const iterator& rhs) const
|
||||
{ return (pos < rhs.pos); }
|
||||
{
|
||||
return (pos < rhs.pos);
|
||||
}
|
||||
|
||||
// derived
|
||||
const T* operator->() const
|
||||
{
|
||||
return &**this;
|
||||
}
|
||||
bool operator!=(const iterator& rhs) const
|
||||
{
|
||||
return !(*this == rhs);
|
||||
}
|
||||
iterator operator++(int) // post
|
||||
{
|
||||
iterator tmp = *this; ++*this; return tmp;
|
||||
}
|
||||
|
||||
protected:
|
||||
T* pos;
|
||||
T* end;
|
||||
// only used when incrementing (avoid going beyond end of table)
|
||||
};
|
||||
|
||||
TChildren::iterator TChildren::begin() const
|
||||
{ return iterator(tbl); }
|
||||
{
|
||||
TNode** pos = tbl;
|
||||
while(pos != tbl+max_entries && *pos == 0)
|
||||
pos++;
|
||||
return iterator(pos, tbl+max_entries);
|
||||
}
|
||||
TChildren::iterator TChildren::end() const
|
||||
{ return iterator(tbl+max_entries); }
|
||||
{
|
||||
return iterator(tbl+max_entries, 0);
|
||||
}
|
||||
|
||||
|
||||
void TChildren::init()
|
||||
@ -236,7 +267,6 @@ void TChildren::init()
|
||||
|
||||
void TChildren::clear()
|
||||
{
|
||||
memset(tbl, 0, max_entries*sizeof(TNode*));
|
||||
free(tbl);
|
||||
tbl = 0;
|
||||
num_entries = max_entries = 0;
|
||||
@ -353,9 +383,12 @@ TNode* TDir::find(const char* fn, TNodeType desired_type)
|
||||
}
|
||||
|
||||
|
||||
TNode* TDir::add(const char* fn, TNodeType new_type)
|
||||
TNode* TDir::add(const char* name, TNodeType new_type)
|
||||
{
|
||||
TNode* node = children.add(fn);
|
||||
if(!path_component_valid(name))
|
||||
return 0;
|
||||
|
||||
TNode* node = children.add(name);
|
||||
if(!node)
|
||||
return 0;
|
||||
// already initialized
|
||||
@ -376,30 +409,21 @@ TNode* TDir::add(const char* fn, TNodeType new_type)
|
||||
|
||||
|
||||
// note: full VFS path is needed for the dir watch.
|
||||
TDir* TDir::add_dir(const char* path, const TMountPoint* mp)
|
||||
int TDir::mount(const char* path, const TMountPoint* mp, bool watch)
|
||||
{
|
||||
const char* subdir_name = strrchr(path, '/');
|
||||
if(!subdir_name++)
|
||||
return 0;
|
||||
|
||||
TNode* node = add(subdir_name, N_DIR);
|
||||
if(!node)
|
||||
return 0;
|
||||
TDir* subdir = &node->u.dir;
|
||||
|
||||
// more than one real dir mounted into VFS dir
|
||||
// (=> can't create files for writing here)
|
||||
if(subdir->mount_point)
|
||||
subdir->mount_point = (TMountPoint*)-1;
|
||||
if(mount_point)
|
||||
mount_point = (TMountPoint*)-1;
|
||||
else
|
||||
subdir->mount_point = mp;
|
||||
mount_point = mp;
|
||||
|
||||
#ifndef NO_DIR_WATCH
|
||||
int ret = res_watch_dir(path, &subdir->watch);
|
||||
assert(ret == 0);
|
||||
if(watch)
|
||||
CHECK_ERR(res_watch_dir(path, &this->watch));
|
||||
#endif
|
||||
|
||||
return subdir;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -500,7 +524,7 @@ void TDir::clearR()
|
||||
for(TChildIt it = children.begin(); it != children.end(); ++it)
|
||||
{
|
||||
TNode* node = *it;
|
||||
if(node && node->type == N_DIR)
|
||||
if(node->type == N_DIR)
|
||||
node->u.dir.clearR();
|
||||
}
|
||||
|
||||
@ -602,9 +626,17 @@ TFile* tree_add_file(TDir* dir, const char* name)
|
||||
}
|
||||
|
||||
|
||||
TDir* tree_add_dir(TDir* dir, const char* path, const TMountPoint* mount_point)
|
||||
TDir* tree_add_dir(TDir* dir, const char* name)
|
||||
{
|
||||
return dir->add_dir(path, mount_point);
|
||||
TNode* node = dir->add(name, N_DIR);
|
||||
return node? &node->u.dir: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int tree_mount(TDir* dir, const char* path, const TMountPoint* mount_point, bool watch)
|
||||
{
|
||||
return dir->mount(path, mount_point, watch);
|
||||
}
|
||||
|
||||
|
||||
@ -648,7 +680,7 @@ struct NodeLatch
|
||||
|
||||
static bool ci_less(const TNode* n1, const TNode* n2)
|
||||
{
|
||||
return stricmp(n1->exact_name, n2->exact_name) <= 0;
|
||||
return stricmp(n1->exact_name, n2->exact_name) < 0;
|
||||
}
|
||||
|
||||
NodeLatch(TChildren& c)
|
||||
@ -656,17 +688,14 @@ struct NodeLatch
|
||||
i = 0;
|
||||
|
||||
v.reserve(c.size());
|
||||
// don't std::copy because c contains a lot of NULL entries
|
||||
// (which we must not return); this way is easier than having the
|
||||
// iterator strip them.
|
||||
for(TChildIt it = c.begin(); it != c.end(); ++it)
|
||||
if(*it)
|
||||
v.push_back(*it);
|
||||
|
||||
std::copy(c.begin(), c.end(), std::back_inserter(v));
|
||||
std::sort(v.begin(), v.end(), ci_less);
|
||||
}
|
||||
|
||||
bool empty() const { return i == v.size(); }
|
||||
bool empty() const
|
||||
{
|
||||
return i == v.size();
|
||||
}
|
||||
|
||||
const TNode* get_next()
|
||||
{
|
||||
@ -686,7 +715,7 @@ int tree_open_dir(const char* path_slash, void** latch)
|
||||
}
|
||||
|
||||
|
||||
int tree_next_dirent(void* latch_, const char* filter, TDirent* dirent)
|
||||
int tree_next_dirent(void* latch_, const char* filter, vfsDirEnt* dirent)
|
||||
{
|
||||
bool want_dir = true;
|
||||
if(filter)
|
||||
|
@ -1,3 +1,26 @@
|
||||
// virtual file system: tree of files and directories
|
||||
//
|
||||
// Copyright (c) 2005 Jan Wassenberg
|
||||
//
|
||||
// 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 2 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.
|
||||
//
|
||||
// Contact info:
|
||||
// Jan.Wassenberg@stud.uni-karlsruhe.de
|
||||
// http://www.stud.uni-karlsruhe.de/~urkt/
|
||||
|
||||
#ifndef VFS_TREE_H__
|
||||
#define VFS_TREE_H__
|
||||
|
||||
#include "vfs.h" // vfsDirEnt
|
||||
|
||||
struct TMountPoint;
|
||||
|
||||
class TDir;
|
||||
@ -26,13 +49,6 @@ struct TFile
|
||||
}
|
||||
};
|
||||
|
||||
// keep in sync with vfs.h vfsDirEnt!
|
||||
struct TDirent
|
||||
{
|
||||
const char* name;
|
||||
off_t size;
|
||||
time_t mtime;
|
||||
};
|
||||
|
||||
enum TreeLookupFlags
|
||||
{
|
||||
@ -47,7 +63,9 @@ extern void tree_clear();
|
||||
extern void tree_display();
|
||||
|
||||
extern TFile* tree_add_file(TDir* dir, const char* name);
|
||||
extern TDir* tree_add_dir(TDir* parent, const char* path, const TMountPoint*);
|
||||
extern TDir * tree_add_dir (TDir* dir, const char* name);
|
||||
|
||||
extern int tree_mount(TDir* dir, const char* path, const TMountPoint*, bool watch = true);
|
||||
|
||||
|
||||
// starting at VFS root, traverse <path> and pass back information
|
||||
@ -83,5 +101,7 @@ extern int tree_lookup_dir(const char* path, TDir** pdir, uint flags = 0, char*
|
||||
extern int tree_lookup(const char* path, TFile** pfile, uint flags = 0, char* exact_path = 0);
|
||||
|
||||
extern int tree_open_dir(const char* path_slash, void** latch);
|
||||
extern int tree_next_dirent(void* latch_, const char* filter, TDirent* dirent);
|
||||
extern int tree_next_dirent(void* latch_, const char* filter, vfsDirEnt* dirent);
|
||||
extern int tree_close_dir(void* latch_);
|
||||
|
||||
#endif // #ifndef VFS_TREE_H__
|
||||
|
@ -41,15 +41,16 @@ bool VFSUtil::FindFiles (CStr& dirname, const char* filter, FileList& files)
|
||||
}
|
||||
|
||||
|
||||
// call <cb> for each file in the <start_path> directory;
|
||||
// if <recursive>, files in subdirectories are also returned.
|
||||
// call <cb> for each entry matching <filter> (see vfs_next_dirent) in the
|
||||
// <start_path> directory; if <recursive>, entries in subdirectories are
|
||||
// also returned.
|
||||
//
|
||||
// note: EnumFileCB path and ent are only valid during the callback.
|
||||
// note: EnumDirEntsCB path and ent are only valid during the callback.
|
||||
int VFSUtil::EnumDirEnts(const CStr start_path, const char* user_filter,
|
||||
bool recursive, EnumDirEntsCB cb, void* context)
|
||||
{
|
||||
// note: currently no need to return subdirectories,
|
||||
// but enabling it isn't hard.
|
||||
// but enabling it isn't hard (we have to check for / anyway).
|
||||
|
||||
char filter_buf[VFS_MAX_PATH];
|
||||
const char* filter = user_filter;
|
||||
|
Loading…
Reference in New Issue
Block a user