15 #include "virtualFileSystem.h" 16 #include "virtualFileSimple.h" 17 #include "virtualFileComposite.h" 18 #include "virtualFileMount.h" 19 #include "virtualFileMountMultifile.h" 20 #include "virtualFileMountRamdisk.h" 21 #include "virtualFileMountSystem.h" 22 #include "streamWrapper.h" 23 #include "dSearchPath.h" 25 #include "config_express.h" 26 #include "executionEnvironment.h" 40 (
"vfs-case-sensitive",
47 PRC_DESC(
"Set this true to make the VirtualFileSystem present the native " 48 "OS-provided filesystem as if it were a case-sensitive file " 49 "system, even if it is not (e.g. on Windows). This variable " 50 "has no effect if the native filesystem is already case-sensitive, " 51 "and it has no effect on mounted multifile systems, which are " 52 "always case-sensitive.")),
54 (
"vfs-implicit-pz", true,
55 PRC_DESC(
"When this is true, the VirtualFileSystem will pretend a named " 56 "file exists even if it doesn't, as long as a filename with the " 57 "same name and the additional extension .pz does exist. In this " 58 "case, the VirtualFileSystem will implicitly open the .pz file " 59 "and decompress it on-the-fly.")),
61 (
"vfs-implicit-mf", false,
62 PRC_DESC(
"When this is true, the VirtualFileSystem will automatically " 63 "mount multifiles on-the-fly when they are used as directories. " 64 "For instance, opening the file /c/files/foo.mf/dirname/mytex.jpg " 65 "will implicitly retrieve a file named 'dirname/mytex.jpg' " 66 "within the multifile /c/files/foo.mf, even if the multifile " 67 "has not already been mounted. This makes all of your multifiles " 68 "act like directories."))
80 ~VirtualFileSystem() {
94 return mount(new_mount, mount_point, flags);
126 int flags,
const string &password) {
127 if (!physical_filename.
exists()) {
128 express_cat->warning()
129 <<
"Attempt to mount " << physical_filename <<
", not found.\n";
136 return mount(new_mount, mount_point, flags);
144 flags |= MF_read_only;
145 if (!multifile->open_read(physical_filename)) {
149 return mount(multifile, mount_point, flags);
173 int flags,
const string &password) {
176 express_cat->warning()
177 <<
"Attempt to mount " << virtual_filename <<
", not found.\n";
181 if (file->is_directory()) {
184 return mount(new_mount, mount_point, flags);
193 flags |= MF_read_only;
194 if (!multifile->open_read(virtual_filename)) {
198 return mount(multifile, mount_point, flags);
212 if (express_cat->is_debug()) {
214 <<
"mount " << *mount <<
" under " << mount_point <<
"\n";
218 bool result = do_mount(mount, mount_point, flags);
233 Mounts::iterator ri, wi;
234 wi = ri = _mounts.begin();
235 while (ri != _mounts.end()) {
239 if (mount->
is_exact_type(VirtualFileMountMultifile::get_class_type())) {
244 if (express_cat->is_debug()) {
246 <<
"unmount " << *mount <<
" from " << mount->
get_mount_point() <<
"\n";
248 mount->_file_system = NULL;
261 int num_removed = _mounts.end() - wi;
262 _mounts.erase(wi, _mounts.end());
278 Mounts::iterator ri, wi;
279 wi = ri = _mounts.begin();
280 while (ri != _mounts.end()) {
284 if (mount->
is_exact_type(VirtualFileMountSystem::get_class_type())) {
289 if (express_cat->is_debug()) {
291 <<
"unmount " << *mount <<
" from " << mount->
get_mount_point() <<
"\n";
293 mount->_file_system = NULL;
300 }
else if (mount->
is_exact_type(VirtualFileMountMultifile::get_class_type())) {
305 if (express_cat->is_debug()) {
307 <<
"unmount " << *mount <<
" from " << mount->
get_mount_point() <<
"\n";
309 mount->_file_system = NULL;
323 int num_removed = _mounts.end() - wi;
324 _mounts.erase(wi, _mounts.end());
340 Mounts::iterator ri, wi;
341 wi = ri = _mounts.begin();
342 while (ri != _mounts.end()) {
344 if ((*ri) ==
mount) {
346 if (express_cat->is_debug()) {
348 <<
"unmount " << *mount <<
" from " << mount->
get_mount_point() <<
"\n";
350 (*ri)->_file_system = NULL;
359 int num_removed = _mounts.end() - wi;
360 _mounts.erase(wi, _mounts.end());
376 Filename nmp = normalize_mount_point(mount_point);
377 Mounts::iterator ri, wi;
378 wi = ri = _mounts.begin();
379 while (ri != _mounts.end()) {
385 if (express_cat->is_debug()) {
387 <<
"unmount " << *mount <<
" from " << mount->
get_mount_point() <<
"\n";
389 mount->_file_system = NULL;
398 int num_removed = _mounts.end() - wi;
399 _mounts.erase(wi, _mounts.end());
415 for (ri = _mounts.begin(); ri != _mounts.end(); ++ri) {
417 if (express_cat->is_debug()) {
419 <<
"unmount " << *mount <<
" from " << mount->
get_mount_point() <<
"\n";
421 mount->_file_system = NULL;
424 int num_removed = _mounts.size();
440 int result = _mounts.size();
453 nassertd(n >= 0 && n < (
int)_mounts.size()) {
473 if (new_directory ==
"/") {
475 _cwd = new_directory;
480 PT(
VirtualFile) file = do_get_file(new_directory, OF_status_only);
481 if (file != (
VirtualFile *)NULL && file->is_directory()) {
482 _cwd = file->get_filename();
515 PT(
VirtualFile) result = do_get_file(filename, OF_make_directory);
517 nassertr_always(result != NULL,
false);
518 return result->is_directory();
535 string dirname = filename;
536 size_t slash = dirname.find(
'/', 1);
537 while (slash != string::npos) {
538 Filename component(dirname.substr(0, slash));
539 do_get_file(component, OF_make_directory);
540 slash = dirname.find(
'/', slash + 1);
544 PT(
VirtualFile) result = do_get_file(filename, OF_make_directory);
546 nassertr_always(result != NULL,
false);
547 return result->is_directory();
568 int open_flags = status_only ? OF_status_only : 0;
570 PT(
VirtualFile) result = do_get_file(filename, open_flags);
588 PT(
VirtualFile) result = do_get_file(filename, OF_create_file);
603 bool status_only)
const {
605 return get_file(filename, status_only);
609 for (
int i = 0; i < num_directories; ++i) {
643 return file->delete_file();
666 PT(
VirtualFile) orig_file = do_get_file(orig_filename, OF_status_only);
672 PT(
VirtualFile) new_file = do_get_file(new_filename, OF_status_only | OF_allow_nonexist);
680 return orig_file->rename_file(new_file);
702 return orig_file->copy_file(new_file);
716 const string &default_extension)
const {
720 found =
find_file(filename, searchpath,
true);
722 if (found.is_null()) {
725 if (filename.
get_extension().empty() && !default_extension.empty()) {
728 found =
find_file(try_ext, searchpath,
true);
738 if (filename.
get_extension().empty() && !default_extension.empty()) {
746 if (!found.is_null()) {
747 filename = found->get_original_filename();
773 for (
int i = 0; i < num_directories; ++i) {
804 Mounts::const_iterator mi;
805 for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) {
837 _global_ptr->
mount(
"/",
"/", 0);
845 PRC_DESC(
"vfs-mount system-filename mount-point [options]"));
848 for (
int i = 0; i < num_unique_values; i++) {
862 size_t space = mount_desc.rfind(
' ');
863 if (space == string::npos) {
864 express_cat.warning()
865 <<
"No space in vfs-mount descriptor: " << mount_desc <<
"\n";
868 string mount_point = mount_desc.substr(space + 1);
869 while (space > 0 && isspace(mount_desc[space - 1])) {
872 mount_desc = mount_desc.substr(0, space);
875 space = mount_desc.rfind(
' ');
876 if (space != string::npos) {
878 options = mount_point;
879 mount_point = mount_desc.substr(space + 1);
880 while (space > 0 && isspace(mount_desc[space - 1])) {
883 mount_desc = mount_desc.substr(0, space);
892 _global_ptr->
mount(physical_filename, mount_point, flags, password);
897 (
"vfs-mount-ramdisk",
"",
898 PRC_DESC(
"vfs-mount-ramdisk mount-point [options]"));
899 if (!vfs_mount_ramdisk.empty()) {
900 string mount_point = vfs_mount_ramdisk;
903 size_t space = mount_point.rfind(
' ');
904 if (space != string::npos) {
906 options = mount_point.substr(space + 1);
907 while (space > 0 && isspace(mount_point[space - 1])) {
910 mount_point = mount_point.substr(0, space);
918 _global_ptr->
mount(ramdisk, mount_point, flags);
945 if (str != (istream *)NULL && str->fail()) {
947 str = (istream *)NULL;
963 if (stream != (istream *)NULL) {
968 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW) 970 (*global_operator_delete)(stream);
996 if (str != (ostream *)NULL && str->fail()) {
998 str = (ostream *)NULL;
1018 if (str != (ostream *)NULL && str->fail()) {
1020 str = (ostream *)NULL;
1036 if (stream != (ostream *)NULL) {
1037 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW) 1039 (*global_operator_delete)(stream);
1060 if (str != (iostream *)NULL && str->fail()) {
1062 str = (iostream *)NULL;
1082 if (str != (iostream *)NULL && str->fail()) {
1084 str = (iostream *)NULL;
1100 if (stream != (iostream *)NULL) {
1101 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW) 1102 stream->~iostream();
1103 (*global_operator_delete)(stream);
1117 const string &old_contents,
1118 const string &new_contents) {
1124 return file->atomic_compare_and_exchange_contents(orig_contents, old_contents, new_contents);
1139 return file->atomic_read_contents(contents);
1155 nassertv(!path.empty() && !path.
is_local());
1157 Mounts::const_iterator mi;
1158 for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) {
1162 if (prefix.empty()) {
1165 if (mount_point.find(
'/') == string::npos) {
1168 names.push_back(mount_point);
1171 if (mount_point.substr(0, prefix.length()) == prefix &&
1172 mount_point.length() > prefix.length() &&
1173 mount_point[prefix.length()] ==
'/') {
1176 string basename = mount_point.substr(prefix.length());
1177 if (basename.find(
'/') == string::npos) {
1179 names.push_back(basename);
1196 password = string();
1200 size_t q = options.find(
',', p);
1201 while (q != string::npos) {
1205 q = options.find(
',', p);
1218 if (option ==
"0" || option.empty()) {
1220 }
else if (option ==
"ro") {
1221 flags |= MF_read_only;
1222 }
else if (option.substr(0, 3) ==
"pw:") {
1223 password = option.substr(3);
1225 express_cat.warning()
1226 <<
"Invalid option on vfs-mount: \"" << option <<
"\"\n";
1241 normalize_mount_point(
const Filename &mount_point)
const {
1247 nassertr(!nmp.empty() && nmp[0] ==
'/', nmp);
1257 bool VirtualFileSystem::
1259 nassertr(mount->_file_system == NULL,
false);
1260 mount->_file_system =
this;
1261 mount->_mount_point = normalize_mount_point(mount_point);
1262 mount->_mount_flags = flags;
1263 _mounts.push_back(mount);
1276 do_get_file(
const Filename &filename,
int open_flags)
const {
1277 if (filename.empty()) {
1282 pathname =
Filename(_cwd, filename);
1291 Filename strpath_pz = strpath +
".pz";
1300 unsigned int start_seq = _mount_seq;
1302 size_t i = _mounts.size();
1307 if (strpath == mount_point) {
1310 if (consider_match(found_file, composite_file, mount,
"", pathname,
1311 false, open_flags)) {
1314 }
else if (mount_point.empty()) {
1316 if (consider_match(found_file, composite_file, mount, strpath,
1317 pathname,
false, open_flags)) {
1321 if (vfs_implicit_pz) {
1322 if (consider_match(found_file, composite_file, mount, strpath_pz,
1323 pathname,
true, open_flags)) {
1329 }
else if (strpath.length() > mount_point.length() &&
1330 mount_point == strpath.substr(0, mount_point.length()) &&
1331 strpath[mount_point.length()] ==
'/') {
1333 Filename local_filename = strpath.substr(mount_point.length() + 1);
1334 Filename local_filename_pz = strpath_pz.substr(mount_point.length() + 1);
1335 if (consider_match(found_file, composite_file, mount, local_filename,
1336 pathname,
false, open_flags)) {
1340 if (vfs_implicit_pz) {
1342 if (consider_match(found_file, composite_file, mount, local_filename_pz,
1343 pathname,
true, open_flags)) {
1353 if (start_seq != _mount_seq) {
1354 start_seq = _mount_seq;
1359 if (found_file == (
VirtualFile *)NULL && vfs_implicit_mf) {
1364 if (start_seq != _mount_seq) {
1367 return do_get_file(filename, open_flags);
1387 bool VirtualFileSystem::
1390 const Filename &original_filename,
bool implicit_pz_file,
1391 int open_flags)
const {
1394 if (!vfile->has_file() && ((open_flags & OF_allow_nonexist) == 0)) {
1402 if (!found_file->
is_directory() || ((open_flags & OF_make_directory) != 0)) {
1407 if (implicit_pz_file) {
1415 if (!vfile->is_directory()) {
1420 if (!implicit_pz_file) {
1428 found_file = composite_file;
1451 bool VirtualFileSystem::
1452 consider_mount_mf(
const Filename &filename) {
1454 if (dirname.empty() || dirname == filename) {
1466 PT(
VirtualFile) file = do_get_file(dirname,
false);
1467 if (file == (
VirtualFile *)NULL || !file->is_regular_file()) {
1474 istream *stream = file->open_read_file(
false);
1475 if (stream == (istream *)NULL) {
1484 if (!multifile->open_read(streamw,
true)) {
1491 <<
"Implicitly mounting " << dirname <<
"\n";
1495 return do_mount(new_mount, dirname, MF_read_only);
1499 return consider_mount_mf(dirname);
Filename get_filename_index(int index) const
If the pattern flag is set for this Filename and the filename string actually includes a sequence of ...
string get_dirname() const
Returns the directory part of the filename.
bool copy_file(const Filename &orig_filename, const Filename &new_filename)
Attempts to copy the contents of the indicated file to the indicated file.
iostream * open_read_append_file(const Filename &filename)
Works like open_read_write_file(), but the file is opened in append mode.
PointerTo< VirtualFile > find_file(const Filename &filename, const DSearchPath &searchpath, bool status_only=false) const
Uses the indicated search path to find the file within the file system.
bool atomic_compare_and_exchange_contents(const Filename &filename, string &orig_contents, const string &old_contents, const string &new_contents)
See Filename::atomic_compare_and_exchange_contents().
string get_basename() const
Returns the basename part of the filename.
void set_extension(const string &s)
Replaces the file extension.
bool rename_file(const Filename &orig_filename, const Filename &new_filename)
Attempts to move or rename the indicated file or directory.
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
virtual ostream * open_append_file()
Works like open_write_file(), but the file is opened in append mode.
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const string &default_extension=string()) const
Searches the given search path for the filename.
Multifile * get_multifile() const
Returns the Multifile pointer that this mount object is based on.
int find_all_files(const Filename &filename, const DSearchPath &searchpath, DSearchPath::Results &results) const
Searches all the directories in the search list for the indicated file, in order. ...
void set_encryption_password(const string &encryption_password)
Specifies the password that will be used to encrypt subfiles subsequently added to the multifile...
PointerTo< VirtualFileMount > get_mount(int n) const
Returns the nth mount in the system.
string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Maps an actual OS directory into the VirtualFileSystem.
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS's file system.
const Filename & get_mount_point() const
Returns the name of the directory within the virtual file system that this mount object is attached t...
istream * open_read_file(const Filename &filename, bool auto_unwrap) const
Convenience function; returns a newly allocated istream if the file exists and can be read...
void add_component(VirtualFile *file)
Adds one more component to the composite directory.
void set_type(Type type)
Sets the type of the file represented by the filename.
virtual ostream * open_write_file(bool auto_wrap, bool truncate)
Opens the file for writing.
void set_binary()
Indicates that the filename represents a binary file.
void set_text()
Indicates that the filename represents a text file.
void set_original_filename(const Filename &filename)
Stores the original filename that was used to locate this VirtualFile.
static void close_read_write_file(iostream *stream)
Closes a file opened by a previous call to open_read_write_file().
static void parse_option(const string &option, int &flags, string &password)
Parses one of the option flags in the options list on the vfs-mount Config.prc line.
int get_num_mounts() const
Returns the number of individual mounts in the system.
bool make_directory_full(const Filename &filename)
Attempts to create a directory within the file system.
The abstract base class for a file or directory within the VirtualFileSystem.
bool is_fully_qualified() const
Returns true if the filename is fully qualified, e.g.
This class is similar to ConfigVariable, but it reports its value as a list of strings.
string get_extension() const
Returns the file extension.
PointerTo< VirtualFile > create_file(const Filename &filename)
Attempts to create a file by the indicated name in the filesystem, if possible, and returns it...
static void close_read_file(istream *stream)
Closes a file opened by a previous call to open_read_file().
void standardize()
Converts the filename to standard form by replacing consecutive slashes with a single slash...
Simulates an actual directory on disk with in-memory storage.
Type get_type() const
Returns the type of the file represented by the filename, as previously set by set_type().
virtual iostream * open_read_write_file(bool truncate)
Opens the file for writing.
int get_num_directories() const
Returns the number of directories on the search list.
bool atomic_read_contents(const Filename &filename, string &contents) const
See Filename::atomic_read_contents().
const Filename & get_directory(int n) const
Returns the nth directory on the search list.
static void parse_options(const string &options, int &flags, string &password)
Parses all of the option flags in the options list on the vfs-mount Config.prc line.
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists.
bool mount(Multifile *multifile, const Filename &mount_point, int flags)
Mounts the indicated Multifile at the given mount point.
bool is_directory(const Filename &filename) const
Convenience function; returns true if the named file exists and is a directory.
The name of a file, such as a texture file or an Egg file.
This is a convenience class to specialize ConfigVariable as a string type.
This class provides a locking wrapper around an arbitrary istream pointer.
bool make_directory(const Filename &filename)
Attempts to create a directory within the file system.
virtual bool is_directory() const
Returns true if this file represents a directory (and scan_directory() may be called), false otherwise.
string get_unique_value(int n) const
Returns the nth unique value of the variable.
bool chdir(const Filename &new_directory)
Changes the current directory.
ostream * open_append_file(const Filename &filename)
Works like open_write_file(), but the file is opened in append mode.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
virtual iostream * open_read_append_file()
Works like open_read_write_file(), but the file is opened in append mode.
void scan_mount_points(vector_string &names, const Filename &path) const
Adds to names a list of all the mount points in use that are one directory below path, if any.
const Filename & get_physical_filename() const
Returns the name of the source file on the OS filesystem of the directory or file that is mounted...
int unmount_point(const Filename &mount_point)
Unmounts all systems attached to the given mount point from the file system.
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
const Filename & get_original_filename() const
Returns the original filename as it was used to locate this VirtualFile.
int unmount(Multifile *multifile)
Unmounts all appearances of the indicated Multifile from the file system.
bool is_local() const
Returns true if the filename is local, e.g.
bool is_text() const
Returns true if the Filename has been indicated to represent a text file via a previous call to set_t...
virtual istream * open_read_file(bool auto_unwrap) const
Opens the file for reading.
bool is_directory() const
Returns true if the filename exists and is a directory name, false otherwise.
void write(ostream &out) const
Print debugging information.
ostream * open_write_file(const Filename &filename, bool auto_wrap, bool truncate)
Convenience function; returns a newly allocated ostream if the file exists and can be written...
The abstract base class for a mount definition used within a VirtualFileSystem.
iostream * open_read_write_file(const Filename &filename, bool truncate)
Convenience function; returns a newly allocated iostream if the file exists and can be written...
A file that contains a set of files.
This class stores a list of directories that can be searched, in order, to locate a particular file...
int get_num_unique_values() const
Returns the number of unique values in the variable.
static Filename get_cwd()
Returns the name of the current working directory.
Filename get_cwd() const
Returns the current directory name.
int unmount_all()
Unmounts all files from the file system.
const Filename & get_multifile_name() const
Returns the filename of the Multifile, if it is available.
static string expand_string(const string &str)
Reads the string, looking for environment variable names marked by a $.
bool delete_file(const Filename &filename)
Attempts to delete the indicated file or directory.
static void close_write_file(ostream *stream)
Closes a file opened by a previous call to open_write_file().
A composite directory within the VirtualFileSystem: this maps to more than one directory on different...
Maps a Multifile's contents into the VirtualFileSystem.
virtual PointerTo< VirtualFile > make_virtual_file(const Filename &local_filename, const Filename &original_filename, bool implicit_pz_file, int open_flags)
Constructs and returns a new VirtualFile instance that corresponds to the indicated filename within t...
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
void add_file(const Filename &file)
Adds a new file to the result list.
bool mount_loop(const Filename &virtual_filename, const Filename &mount_point, int flags, const string &password="")
This is similar to mount(), but it receives the name of a Multifile that already appears within the v...
static Filename from_os_specific(const string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes, and no drive letter) based on the supplied filename string that describes a filename in the local system conventions (for instance, on Windows, it may use backslashes or begin with a drive letter and a colon).