2005-03-27 03:46:54 +02:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
|
|
|
#include <string.h>
|
2005-06-01 22:12:45 +02:00
|
|
|
#include <time.h>
|
2005-03-27 03:46:54 +02:00
|
|
|
|
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
2005-06-01 22:12:45 +02:00
|
|
|
#include <algorithm>
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
#include "lib/allocators.h"
|
|
|
|
#include "lib/adts.h"
|
2006-01-25 08:21:45 +01:00
|
|
|
#include "file_internal.h"
|
2005-03-27 03:46:54 +02:00
|
|
|
|
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
// we add/cancel directory watches from the VFS mount code for convenience -
|
|
|
|
// it iterates through all subdirectories anyway (*) and provides storage for
|
|
|
|
// a key to identify the watch (obviates separate TDir -> watch mapping).
|
|
|
|
//
|
|
|
|
// define this to strip out that code - removes .watch from struct TDir,
|
|
|
|
// and calls to res_watch_dir / res_cancel_watch.
|
|
|
|
//
|
|
|
|
// *: the add_watch code would need to iterate through subdirs and watch
|
|
|
|
// each one, because the monitor API (e.g. FAM) may only be able to
|
|
|
|
// watch single directories, instead of a whole subdirectory tree.
|
|
|
|
//#define NO_DIR_WATCH
|
|
|
|
|
|
|
|
|
|
|
|
// Mount = location of a file in the tree.
|
2005-03-27 03:46:54 +02:00
|
|
|
// TFile = all information about a file stored in the tree.
|
|
|
|
// TDir = container holding TFile-s representing a dir. in the tree.
|
|
|
|
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
static void* node_alloc();
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// locking
|
|
|
|
// these are exported to protect the vfs_mount list; apart from that, it is
|
|
|
|
// sufficient for VFS thread-safety to lock all of this module's APIs.
|
|
|
|
|
|
|
|
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
|
|
|
|
void tree_lock()
|
|
|
|
{
|
|
|
|
pthread_mutex_lock(&mutex);
|
|
|
|
}
|
|
|
|
|
|
|
|
void tree_unlock()
|
|
|
|
{
|
|
|
|
pthread_mutex_unlock(&mutex);
|
|
|
|
}
|
|
|
|
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
//-----------------------------------------------------------------------------
|
2005-08-09 18:32:23 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
enum TNodeType
|
|
|
|
{
|
|
|
|
NT_DIR,
|
|
|
|
NT_FILE
|
|
|
|
};
|
2005-08-09 18:32:23 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
class TNode
|
2005-08-09 18:32:23 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
public:
|
|
|
|
TNodeType type;
|
2005-08-09 18:32:23 +02:00
|
|
|
|
2006-01-31 04:47:52 +01:00
|
|
|
// rationale: we store both entire path and name component.
|
|
|
|
// this increases size of VFS (2 pointers needed here) and
|
|
|
|
// filename storage, but allows getting path without having to
|
|
|
|
// iterate over all dir name components.
|
|
|
|
//we could retrieve name via strrchr(path, '/'), but that is slow.
|
|
|
|
PathName V_path;
|
|
|
|
|
|
|
|
TNode(TNodeType type_, PathName V_path_)
|
|
|
|
: type(type_), V_path(V_path_) {}
|
2005-08-09 18:32:23 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
class TFile : public TNode
|
2005-03-27 03:46:54 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
public:
|
|
|
|
// required:
|
|
|
|
const Mount* m;
|
|
|
|
// allocated and owned by caller (mount code)
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
off_t size;
|
|
|
|
time_t mtime;
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
uintptr_t memento;
|
2005-04-10 00:25:23 +02:00
|
|
|
|
2006-01-31 04:47:52 +01:00
|
|
|
TFile(PathName V_path, const Mount* m_)
|
|
|
|
: TNode(NT_FILE, V_path)
|
2005-03-27 19:27:49 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
m = m_;
|
|
|
|
size = 0;
|
|
|
|
mtime = 0;
|
|
|
|
memento = 0;
|
2005-03-27 19:27:49 +02:00
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
};
|
2005-04-10 00:25:23 +02:00
|
|
|
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
template<> class DHT_Traits<const char*, TNode*>
|
|
|
|
{
|
2005-04-10 00:25:23 +02:00
|
|
|
public:
|
2006-01-23 21:05:09 +01:00
|
|
|
static const size_t initial_entries = 16;
|
|
|
|
size_t hash(const char* key) const
|
2005-03-27 19:27:49 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
return (size_t)fnv_lc_hash(key);
|
2005-03-27 19:27:49 +02:00
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
bool equal(const char* k1, const char* k2) const
|
2005-03-27 19:27:49 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
// exact match
|
|
|
|
if(!strcmp(k1, k2))
|
|
|
|
return true;
|
|
|
|
#ifndef NDEBUG
|
|
|
|
// matched except for case: this can have 2 causes:
|
|
|
|
// - intentional. that would be legitimate but doesn't make much
|
|
|
|
// sense and isn't expected.
|
|
|
|
// - bug, e.g. discarding filename case in a filelist.
|
|
|
|
// this risks not being able to find the file (since VFS and
|
|
|
|
// possibly OS are case-sensitive) and wastes memory here.
|
|
|
|
// what we'll do is warn and treat as separate filename
|
|
|
|
// (least surprise).
|
2006-01-24 09:16:29 +01:00
|
|
|
// if(!stricmp(k1, k2))
|
|
|
|
// debug_warn("filenames differ only in case: bug?");
|
2006-01-23 21:05:09 +01:00
|
|
|
#endif
|
|
|
|
return false;
|
2005-03-27 19:27:49 +02:00
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
const char* get_key(TNode* t) const
|
2005-03-27 19:27:49 +02:00
|
|
|
{
|
2006-01-31 04:47:52 +01:00
|
|
|
return t->V_path.name;
|
2005-03-27 19:27:49 +02:00
|
|
|
}
|
2006-01-23 21:05:09 +01:00
|
|
|
};
|
|
|
|
typedef DynHashTbl<const char*, TNode*, DHT_Traits<const char*, TNode*> > TChildren;
|
|
|
|
typedef TChildren::iterator TChildrenIt;
|
2005-04-10 00:25:23 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
enum TDirFlags
|
|
|
|
{
|
|
|
|
TD_POPULATED = 1
|
|
|
|
};
|
2005-04-10 00:25:23 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
class TDir : public TNode
|
|
|
|
{
|
|
|
|
int flags; // enum TDirFlags
|
2005-04-10 00:25:23 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
RealDir rd;
|
2005-04-10 00:25:23 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
TChildren children;
|
2005-03-27 19:27:49 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
public:
|
2006-01-31 04:47:52 +01:00
|
|
|
TDir(PathName V_path)
|
|
|
|
: TNode(NT_DIR, V_path), children()
|
2005-03-27 19:27:49 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
flags = 0;
|
|
|
|
rd.m = 0;
|
|
|
|
rd.watch = 0;
|
2005-03-27 19:27:49 +02:00
|
|
|
}
|
2005-04-10 00:25:23 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
TNode* find(const char* name) const { return children.find(name); }
|
|
|
|
TChildrenIt begin() const { return children.begin(); }
|
|
|
|
TChildrenIt end() const { return children.end(); }
|
2005-04-10 00:25:23 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
// non-const - caller may change e.g. rd.watch
|
|
|
|
RealDir& get_rd() { return rd; }
|
2005-04-10 00:25:23 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
void populate()
|
2005-03-27 19:27:49 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
// the caller may potentially access this directory.
|
|
|
|
// make sure it has been populated with loose files/directories.
|
|
|
|
if(!(flags & TD_POPULATED))
|
2005-04-10 00:25:23 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
WARN_ERR(mount_populate(this, &rd));
|
|
|
|
flags |= TD_POPULATED;
|
2005-04-10 00:25:23 +02:00
|
|
|
}
|
2005-03-27 19:27:49 +02:00
|
|
|
}
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-31 04:47:52 +01:00
|
|
|
|
|
|
|
LibError add(const char* name_tmp, TNodeType type, TNode** pnode)
|
2006-01-23 21:05:09 +01:00
|
|
|
{
|
2006-01-31 04:47:52 +01:00
|
|
|
char V_new_path_tmp[VFS_MAX_PATH];
|
|
|
|
vfs_path_append(V_new_path_tmp, V_path.path, name_tmp);
|
|
|
|
PathName V_new_path;
|
|
|
|
pathname_split(V_new_path_tmp, &V_new_path);
|
|
|
|
const char* name = V_new_path.name;
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
if(!path_component_valid(name))
|
|
|
|
return ERR_PATH_INVALID;
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
TNode* node = children.find(name);
|
|
|
|
if(node)
|
|
|
|
{
|
|
|
|
if(node->type != type)
|
|
|
|
return (type == NT_FILE)? ERR_NOT_FILE : ERR_NOT_DIR;
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
*pnode = node;
|
|
|
|
return INFO_ALREADY_PRESENT;
|
|
|
|
}
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
// note: if anything below fails, this mem remains allocated in the
|
|
|
|
// pool, but that "can't happen" and is OK because pool is big enough.
|
|
|
|
void* mem = node_alloc();
|
|
|
|
if(!mem)
|
|
|
|
return ERR_NO_MEM;
|
|
|
|
#include "nommgr.h"
|
|
|
|
if(type == NT_FILE)
|
2006-01-31 04:47:52 +01:00
|
|
|
node = new(mem) TFile(V_new_path, rd.m);
|
2006-01-23 21:05:09 +01:00
|
|
|
else
|
2006-01-31 04:47:52 +01:00
|
|
|
node = new(mem) TDir(V_new_path);
|
2006-01-23 21:05:09 +01:00
|
|
|
#include "mmgr.h"
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
children.insert(name, node);
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
*pnode = node;
|
|
|
|
return ERR_OK;
|
|
|
|
}
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
// empty this directory and all subdirectories; used when rebuilding VFS.
|
|
|
|
void clearR()
|
|
|
|
{
|
|
|
|
// recurse for all subdirs
|
|
|
|
// (preorder traversal - need to do this before clearing the list)
|
|
|
|
for(TChildrenIt it = children.begin(); it != children.end(); ++it)
|
|
|
|
{
|
|
|
|
TNode* node = *it;
|
|
|
|
if(node->type == NT_DIR)
|
|
|
|
((TDir*)node)->clearR();
|
|
|
|
}
|
2005-08-13 19:09:57 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
// wipe out this directory
|
|
|
|
children.clear();
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
// the watch is restored when this directory is repopulated; we must
|
|
|
|
// remove it in case the real directory backing this one was deleted.
|
|
|
|
mount_detach_real_dir(&rd);
|
|
|
|
}
|
2005-04-10 00:25:23 +02:00
|
|
|
};
|
2005-03-27 03:46:54 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
|
2005-03-27 03:46:54 +02:00
|
|
|
|
|
|
|
|
2005-04-10 00:25:23 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
static Pool node_pool;
|
2005-04-10 00:25:23 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
static inline void node_init()
|
2005-04-10 00:25:23 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
const size_t el_size = MAX(sizeof(TDir), sizeof(TFile));
|
|
|
|
(void)pool_create(&node_pool, VFS_MAX_FILES*el_size, el_size);
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
static inline void node_shutdown()
|
2005-04-10 00:25:23 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
(void)pool_destroy(&node_pool);
|
2005-04-10 00:25:23 +02:00
|
|
|
}
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
static void* node_alloc()
|
2005-03-27 03:46:54 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
return pool_alloc(&node_pool, 0);
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
static void displayR(TDir* td, int indent_level)
|
2005-03-27 03:46:54 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
const char indent[] = " ";
|
2005-03-27 19:27:49 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
TChildrenIt it;
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
// list all files in this dir
|
|
|
|
for(it = td->begin(); it != td->end(); ++it)
|
2005-08-09 18:32:23 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
TNode* node = (*it);
|
|
|
|
if(node->type != NT_FILE)
|
|
|
|
continue;
|
2006-01-31 04:47:52 +01:00
|
|
|
const char* name = node->V_path.name;
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
TFile& file = *((TFile*)node);
|
|
|
|
char file_location = mount_get_type(file.m);
|
|
|
|
char* timestamp = ctime(&file.mtime);
|
|
|
|
timestamp[24] = '\0'; // remove '\n'
|
|
|
|
const off_t size = file.size;
|
|
|
|
|
|
|
|
// build format string: tell it how long the filename may be,
|
|
|
|
// so that it takes up all space before file info column.
|
|
|
|
char fmt[25];
|
|
|
|
int chars = 80 - indent_level*(sizeof(indent)-1);
|
|
|
|
sprintf(fmt, "%%-%d.%ds (%%c; %%6d; %%s)\n", chars, chars);
|
2005-04-10 00:25:23 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
for(int i = 0; i < indent_level; i++)
|
|
|
|
printf(indent);
|
|
|
|
printf(fmt, name, file_location, size, timestamp);
|
2005-08-09 18:32:23 +02:00
|
|
|
}
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
// recurse over all subdirs
|
|
|
|
for(it = td->begin(); it != td->end(); ++it)
|
|
|
|
{
|
|
|
|
TNode* node = (*it);
|
|
|
|
if(node->type != NT_DIR)
|
|
|
|
continue;
|
2006-01-31 04:47:52 +01:00
|
|
|
const char* subdir_name = node->V_path.name;
|
2006-01-23 21:05:09 +01:00
|
|
|
|
|
|
|
// write subdir's name
|
|
|
|
// note: do it now, instead of in recursive call so that:
|
|
|
|
// - we don't have to pass dir_name parameter;
|
|
|
|
// - the VFS root node isn't displayed.
|
|
|
|
for(int i = 0; i < indent_level; i++)
|
|
|
|
printf(indent);
|
|
|
|
printf("[%s/]\n", subdir_name);
|
|
|
|
|
|
|
|
TDir* subdir = ((TDir*)node);
|
|
|
|
displayR(subdir, indent_level+1);
|
|
|
|
}
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
static LibError lookup(TDir* td, const char* path, uint flags, TNode** pnode)
|
|
|
|
{
|
2005-03-27 03:46:54 +02:00
|
|
|
// early out: "" => return this directory (usually VFS root)
|
|
|
|
if(path[0] == '\0')
|
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
*pnode = (TNode*)td; // HACK: TDir is at start of TNode
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
CHECK_PATH(path);
|
2005-06-28 06:06:25 +02:00
|
|
|
debug_assert( (flags & ~(LF_CREATE_MISSING|LF_START_DIR)) == 0 );
|
2005-08-09 18:32:23 +02:00
|
|
|
// no undefined bits set
|
2005-03-27 03:46:54 +02:00
|
|
|
|
|
|
|
const bool create_missing = !!(flags & LF_CREATE_MISSING);
|
|
|
|
|
|
|
|
// copy into (writeable) buffer so we can 'tokenize' path components
|
|
|
|
// by replacing '/' with '\0'.
|
2006-01-23 21:05:09 +01:00
|
|
|
char V_path[VFS_MAX_PATH];
|
|
|
|
strcpy_s(V_path, sizeof(V_path), path);
|
|
|
|
char* cur_component = V_path;
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
TNodeType type = NT_DIR;
|
2005-03-27 03:46:54 +02:00
|
|
|
|
|
|
|
// successively navigate to the next component in <path>.
|
2005-07-04 19:03:08 +02:00
|
|
|
TNode* node = 0;
|
2005-03-27 03:46:54 +02:00
|
|
|
for(;;)
|
|
|
|
{
|
|
|
|
// "extract" cur_component string (0-terminate by replacing '/')
|
|
|
|
char* slash = (char*)strchr(cur_component, '/');
|
|
|
|
if(!slash)
|
|
|
|
{
|
2005-07-04 19:03:08 +02:00
|
|
|
// all other node assignments are checked, so this must have
|
|
|
|
// been the first iteration and there's no slash =>
|
|
|
|
// pathname is incorrect.
|
|
|
|
if(!node)
|
|
|
|
return ERR_INVALID_PARAM;
|
|
|
|
|
2005-07-02 23:42:55 +02:00
|
|
|
// string ended in slash => return the current dir node.
|
2005-03-27 03:46:54 +02:00
|
|
|
if(*cur_component == '\0')
|
|
|
|
break;
|
2005-07-04 19:03:08 +02:00
|
|
|
|
2005-03-27 03:46:54 +02:00
|
|
|
// it's a filename
|
2006-01-23 21:05:09 +01:00
|
|
|
type = NT_FILE;
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
// normal operation (cur_component is a directory)
|
|
|
|
else
|
2005-08-09 18:32:23 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
td->populate();
|
2005-08-09 18:32:23 +02:00
|
|
|
|
2005-03-27 03:46:54 +02:00
|
|
|
*slash = '\0';
|
2005-08-09 18:32:23 +02:00
|
|
|
}
|
2005-03-27 03:46:54 +02:00
|
|
|
|
|
|
|
// create <cur_component> (no-op if it already exists)
|
|
|
|
if(create_missing)
|
2006-01-23 21:05:09 +01:00
|
|
|
RETURN_ERR(td->add(V_path, type, &node));
|
2005-03-27 03:46:54 +02:00
|
|
|
else
|
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
node = td->find(cur_component);
|
2005-03-27 03:46:54 +02:00
|
|
|
if(!node)
|
|
|
|
return slash? ERR_PATH_NOT_FOUND : ERR_FILE_NOT_FOUND;
|
2006-01-23 21:05:09 +01:00
|
|
|
if(node->type != type)
|
|
|
|
return slash? ERR_NOT_DIR : ERR_NOT_FILE;
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// cur_component was a filename => we're done
|
|
|
|
if(!slash)
|
|
|
|
break;
|
|
|
|
// else: it was a directory; advance
|
2006-01-23 21:05:09 +01:00
|
|
|
// .. undo having replaced '/' with '\0' - this means V_path will
|
|
|
|
// store the complete path up to and including cur_component.
|
2006-01-24 09:16:29 +01:00
|
|
|
*slash = '/';
|
2005-03-27 03:46:54 +02:00
|
|
|
cur_component = slash+1;
|
2006-01-23 21:05:09 +01:00
|
|
|
td = (TDir*)node;
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// success.
|
|
|
|
*pnode = node;
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2006-01-31 04:47:52 +01:00
|
|
|
static const PathName pn = { "", "" };
|
|
|
|
static TDir tree_root(pn);
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
// rationale: can't do this in tree_shutdown - we'd leak at exit.
|
|
|
|
// calling from tree_add* is ugly as well, so require manual init.
|
|
|
|
void tree_init()
|
2005-03-27 03:46:54 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
node_init();
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-10-21 09:47:38 +02:00
|
|
|
void tree_shutdown()
|
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
node_shutdown();
|
2005-10-21 09:47:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
void tree_clear()
|
2005-03-27 03:46:54 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
tree_root.clearR();
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
// write a representation of the VFS tree to stdout.
|
|
|
|
void tree_display()
|
|
|
|
{
|
|
|
|
displayR(&tree_root, 0);
|
|
|
|
}
|
2005-08-09 18:32:23 +02:00
|
|
|
|
|
|
|
|
2006-01-31 04:47:52 +01:00
|
|
|
LibError tree_add_file(TDir* td, const char* name,
|
2006-01-23 21:05:09 +01:00
|
|
|
const Mount* m, off_t size, time_t mtime, uintptr_t memento)
|
2005-03-27 03:46:54 +02:00
|
|
|
{
|
2005-08-09 18:32:23 +02:00
|
|
|
TNode* node;
|
2006-01-31 04:47:52 +01:00
|
|
|
LibError ret = td->add(name, NT_FILE, &node);
|
2006-01-23 21:05:09 +01:00
|
|
|
RETURN_ERR(ret);
|
2006-01-31 08:48:32 +01:00
|
|
|
if(ret == INFO_ALREADY_PRESENT)
|
2006-01-23 21:05:09 +01:00
|
|
|
{
|
|
|
|
// assume they're the same if size and last-modified time match.
|
|
|
|
// note: FAT timestamp only has 2 second resolution
|
|
|
|
TFile* tf = (TFile*)node;
|
|
|
|
const bool is_same = (tf->size == size) &&
|
|
|
|
fabs(difftime(tf->mtime, mtime)) <= 2.0;
|
|
|
|
if(!mount_should_replace(tf->m, m, is_same))
|
2006-01-31 08:48:32 +01:00
|
|
|
return INFO_ALREADY_PRESENT;
|
|
|
|
|
|
|
|
stats_vfs_file_remove(tf->size);
|
2006-01-23 21:05:09 +01:00
|
|
|
}
|
2005-08-09 18:32:23 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
TFile* tf = (TFile*)node;
|
|
|
|
tf->m = m;
|
|
|
|
tf->mtime = mtime;
|
|
|
|
tf->size = size;
|
|
|
|
tf->memento = memento;
|
2006-01-31 08:48:32 +01:00
|
|
|
stats_vfs_file_add(size);
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-31 04:47:52 +01:00
|
|
|
LibError tree_add_dir(TDir* td, const char* name, TDir** ptd)
|
2005-03-27 03:46:54 +02:00
|
|
|
{
|
2005-08-09 18:32:23 +02:00
|
|
|
TNode* node;
|
2006-01-31 04:47:52 +01:00
|
|
|
RETURN_ERR(td->add(name, NT_DIR, &node));
|
2006-01-23 21:05:09 +01:00
|
|
|
*ptd = (TDir*)node;
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2005-03-27 19:27:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
LibError tree_lookup_dir(const char* path, TDir** ptd, uint flags)
|
2005-03-27 03:46:54 +02:00
|
|
|
{
|
2005-12-11 23:23:55 +01:00
|
|
|
// path is not a directory; TDir::lookup might return a file node
|
2005-03-27 03:46:54 +02:00
|
|
|
if(path[0] != '\0' && path[strlen(path)-1] != '/')
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_NOT_DIR;
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
TDir* td = (flags & LF_START_DIR)? *ptd : &tree_root;
|
2005-03-27 03:46:54 +02:00
|
|
|
TNode* node;
|
2006-01-23 21:05:09 +01:00
|
|
|
CHECK_ERR(lookup(td, path, flags, &node));
|
2005-03-27 19:40:40 +02:00
|
|
|
// directories should exist, so warn if this fails
|
2006-01-23 21:05:09 +01:00
|
|
|
*ptd = (TDir*)node;
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
LibError tree_lookup(const char* path, TFile** pfile, uint flags)
|
2005-03-27 03:46:54 +02:00
|
|
|
{
|
2005-12-11 23:23:55 +01:00
|
|
|
// path is not a file; TDir::lookup might return a directory node
|
2005-03-27 03:46:54 +02:00
|
|
|
if(path[0] == '\0' || path[strlen(path)-1] == '/')
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_NOT_FILE;
|
2005-03-27 03:46:54 +02:00
|
|
|
|
|
|
|
TNode* node;
|
2006-01-23 21:05:09 +01:00
|
|
|
LibError ret = lookup(&tree_root, path, flags, &node);
|
2005-03-27 19:40:40 +02:00
|
|
|
RETURN_ERR(ret);
|
2006-01-23 21:05:09 +01:00
|
|
|
*pfile = (TFile*)node;
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
// rationale: see DirIterator definition in file.h.
|
|
|
|
struct TreeDirIterator_
|
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
TChildren::iterator it;
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
// cache end() to avoid needless copies
|
2006-01-23 21:05:09 +01:00
|
|
|
TChildren::iterator end;
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
// the directory we're iterating over; this is used to lock/unlock it,
|
|
|
|
// i.e. prevent modifications that would invalidate the iterator.
|
|
|
|
TDir* td;
|
2005-03-27 03:46:54 +02:00
|
|
|
};
|
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
cassert(sizeof(TreeDirIterator_) <= sizeof(TreeDirIterator));
|
2005-03-27 03:46:54 +02:00
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
LibError tree_dir_open(const char* path_slash, TreeDirIterator* d_)
|
2005-03-27 03:46:54 +02:00
|
|
|
{
|
2005-08-09 18:32:23 +02:00
|
|
|
TreeDirIterator_* d = (TreeDirIterator_*)d_;
|
|
|
|
|
|
|
|
TDir* td;
|
|
|
|
CHECK_ERR(tree_lookup_dir(path_slash, &td));
|
|
|
|
|
|
|
|
// we need to prevent modifications to this directory while an iterator is
|
|
|
|
// active, otherwise entries may be skipped or no longer valid addresses
|
|
|
|
// accessed. blocking other threads is much more convenient for callers
|
|
|
|
// than having to check for ERR_AGAIN on every call, so we use a mutex
|
|
|
|
// instead of a simple refcount. we don't bother with fine-grained locking
|
|
|
|
// (e.g. per directory or read/write locks) because it would result in
|
|
|
|
// more overhead (we have hundreds of directories) and is unnecessary.
|
|
|
|
tree_lock();
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
d->it = td->begin();
|
|
|
|
d->end = td->end();
|
2005-08-09 18:32:23 +02:00
|
|
|
d->td = td;
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
LibError tree_dir_next_ent(TreeDirIterator* d_, DirEnt* ent)
|
2005-03-27 03:46:54 +02:00
|
|
|
{
|
2005-08-09 18:32:23 +02:00
|
|
|
TreeDirIterator_* d = (TreeDirIterator_*)d_;
|
|
|
|
|
|
|
|
if(d->it == d->end)
|
|
|
|
return ERR_DIR_END;
|
|
|
|
|
|
|
|
const TNode* node = *(d->it++);
|
2006-01-31 04:47:52 +01:00
|
|
|
ent->name = node->V_path.name;
|
2005-08-09 18:32:23 +02:00
|
|
|
|
|
|
|
// set size and mtime fields depending on node type:
|
|
|
|
switch(node->type)
|
2005-03-27 03:46:54 +02:00
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
case NT_DIR:
|
2005-08-09 18:32:23 +02:00
|
|
|
ent->size = -1;
|
|
|
|
ent->mtime = 0; // not currently supported for dirs
|
|
|
|
break;
|
2006-01-23 21:05:09 +01:00
|
|
|
case NT_FILE:
|
|
|
|
{
|
|
|
|
TFile* tf = (TFile*)node;
|
|
|
|
ent->size = tf->size;
|
|
|
|
ent->mtime = tf->mtime;
|
2005-08-09 18:32:23 +02:00
|
|
|
break;
|
2006-01-23 21:05:09 +01:00
|
|
|
}
|
2005-08-09 18:32:23 +02:00
|
|
|
default:
|
2005-10-19 08:29:55 +02:00
|
|
|
debug_warn("invalid TNode type");
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2005-08-09 18:32:23 +02:00
|
|
|
}
|
2005-03-27 03:46:54 +02:00
|
|
|
|
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
LibError tree_dir_close(TreeDirIterator* UNUSED(d))
|
2005-08-09 18:32:23 +02:00
|
|
|
{
|
|
|
|
tree_unlock();
|
|
|
|
|
|
|
|
// no further cleanup needed. we could zero out d but that might
|
|
|
|
// hide bugs; the iterator is safe (will not go beyond end) anyway.
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
//-----------------------------------------------------------------------------
|
2005-08-13 19:09:57 +02:00
|
|
|
// get/set
|
2005-08-09 18:32:23 +02:00
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
const Mount* tfile_get_mount(const TFile* tf)
|
2005-08-09 18:32:23 +02:00
|
|
|
{
|
|
|
|
return tf->m;
|
|
|
|
}
|
|
|
|
|
2006-01-23 21:05:09 +01:00
|
|
|
uintptr_t tfile_get_memento(const TFile* tf)
|
|
|
|
{
|
|
|
|
return tf->memento;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* tfile_get_atom_fn(const TFile* tf)
|
|
|
|
{
|
2006-01-31 04:47:52 +01:00
|
|
|
return ((TNode*)tf)->V_path.path;
|
2006-01-23 21:05:09 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-08-09 18:32:23 +02:00
|
|
|
|
|
|
|
void tree_update_file(TFile* tf, off_t size, time_t mtime)
|
2005-03-27 03:46:54 +02:00
|
|
|
{
|
2005-08-09 18:32:23 +02:00
|
|
|
tf->size = size;
|
|
|
|
tf->mtime = mtime;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// get file status (mode, size, mtime). output param is undefined on error.
|
2005-12-11 23:23:55 +01:00
|
|
|
LibError tree_stat(const TFile* tf, struct stat* s)
|
2005-08-09 18:32:23 +02:00
|
|
|
{
|
|
|
|
// all stat members currently supported are stored in TFile, so we
|
|
|
|
// can return them directly without having to call file|zip_stat.
|
|
|
|
s->st_mode = S_IFREG;
|
|
|
|
s->st_size = tf->size;
|
|
|
|
s->st_mtime = tf->mtime;
|
|
|
|
|
2005-12-11 23:23:55 +01:00
|
|
|
return ERR_OK;
|
2005-03-27 03:46:54 +02:00
|
|
|
}
|
2005-08-13 19:09:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
RealDir* tree_get_real_dir(TDir* td)
|
|
|
|
{
|
2006-01-23 21:05:09 +01:00
|
|
|
return &td->get_rd();
|
2005-08-13 19:09:57 +02:00
|
|
|
}
|