Panda3D
Loading...
Searching...
No Matches
virtualFileSystem.cxx
Go to the documentation of this file.
1/**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file virtualFileSystem.cxx
10 * @author drose
11 * @date 2002-08-03
12 */
13
14#include "virtualFileSystem.h"
15#include "virtualFileSimple.h"
17#include "virtualFileMount.h"
21#include "streamWrapper.h"
22#include "dSearchPath.h"
23#include "dcast.h"
24#include "config_express.h"
25#include "configVariableList.h"
28#include "pset.h"
29
30using std::iostream;
31using std::istream;
32using std::ostream;
33using std::string;
34
35VirtualFileSystem *VirtualFileSystem::_global_ptr = nullptr;
36
37
38/**
39 *
40 */
41VirtualFileSystem::
42VirtualFileSystem() :
43 vfs_case_sensitive
44("vfs-case-sensitive",
45#ifdef NDEBUG
46 false, // The default for a production build is not case-sensitive;
47 // this avoids runtime overhead to verify case sensitivity.
48#else
49 true,
50#endif
51 PRC_DESC("Set this true to make the VirtualFileSystem present the native "
52 "OS-provided filesystem as if it were a case-sensitive file "
53 "system, even if it is not (e.g. on Windows). This variable "
54 "has no effect if the native filesystem is already case-sensitive, "
55 "and it has no effect on mounted multifile systems, which are "
56 "always case-sensitive.")),
57 vfs_implicit_pz
58 ("vfs-implicit-pz", true,
59 PRC_DESC("When this is true, the VirtualFileSystem will pretend a named "
60 "file exists even if it doesn't, as long as a filename with the "
61 "same name and the additional extension .pz does exist. In this "
62 "case, the VirtualFileSystem will implicitly open the .pz file "
63 "and decompress it on-the-fly.")),
64 vfs_implicit_mf
65 ("vfs-implicit-mf", false,
66 PRC_DESC("When this is true, the VirtualFileSystem will automatically "
67 "mount multifiles on-the-fly when they are used as directories. "
68 "For instance, opening the file /c/files/foo.mf/dirname/mytex.jpg "
69 "will implicitly retrieve a file named 'dirname/mytex.jpg' "
70 "within the multifile /c/files/foo.mf, even if the multifile "
71 "has not already been mounted. This makes all of your multifiles "
72 "act like directories."))
73{
74 _cwd = "/";
75 _mount_seq = 0;
76}
77
78/**
79 *
80 */
81VirtualFileSystem::
82~VirtualFileSystem() {
84}
85
86/**
87 * Mounts the indicated Multifile at the given mount point.
88 */
90mount(Multifile *multifile, const Filename &mount_point, int flags) {
91 PT(VirtualFileMountMultifile) new_mount =
92 new VirtualFileMountMultifile(multifile);
93 return mount(new_mount, mount_point, flags);
94}
95
96/**
97 * Mounts the indicated system file or directory at the given mount point. If
98 * the named file is a directory, mounts the directory. If the named file is
99 * a Multifile, mounts it as a Multifile. Returns true on success, false on
100 * failure.
101 *
102 * A given system directory may be mounted to multiple different mount point,
103 * and the same mount point may share multiple system directories. In the
104 * case of ambiguities (that is, two different files with exactly the same
105 * full pathname), the most-recently mounted system wins.
106 *
107 * The filename specified as the first parameter must refer to a real,
108 * physical filename on disk; it cannot be a virtual file already appearing
109 * within the vfs filespace. However, it is possible to mount such a file;
110 * see mount_loop() for this.
111 *
112 * Note that a mounted VirtualFileSystem directory is fully case-sensitive,
113 * unlike the native Windows file system, so you must refer to files within
114 * the virtual file system with exactly the right case.
115 */
117mount(const Filename &physical_filename, const Filename &mount_point,
118 int flags, const string &password) {
119 if (!physical_filename.exists()) {
120 express_cat->warning()
121 << "Attempt to mount " << physical_filename << ", not found.\n";
122 return false;
123 }
124
125 if (physical_filename.is_directory()) {
126 PT(VirtualFileMountSystem) new_mount =
127 new VirtualFileMountSystem(physical_filename);
128 return mount(new_mount, mount_point, flags);
129 } else {
130 // It's not a directory; it must be a Multifile.
131 PT(Multifile) multifile = new Multifile;
132 multifile->set_encryption_password(password);
133
134 // For now these are always opened read only. Maybe later we'll support
135 // read-write on Multifiles.
136 flags |= MF_read_only;
137 if (!multifile->open_read(physical_filename)) {
138 return false;
139 }
140
141 return mount(multifile, mount_point, flags);
142 }
143}
144
145/**
146 * This is similar to mount(), but it receives the name of a Multifile that
147 * already appears within the virtual file system. It can be used to mount a
148 * Multifile that is itself hosted within a virtually-mounted Multifile.
149 *
150 * This interface can also be used to mount physical files (that appear within
151 * the virtual filespace), but it cannot be used to mount directories. Use
152 * mount() if you need to mount a directory.
153 *
154 * Note that there is additional overhead, in the form of additional buffer
155 * copies of the data, for recursively mounting a multifile like this.
156 */
158mount_loop(const Filename &virtual_filename, const Filename &mount_point,
159 int flags, const string &password) {
160 PT(VirtualFile) file = get_file(virtual_filename, false);
161 if (file == nullptr) {
162 express_cat->warning()
163 << "Attempt to mount " << virtual_filename << ", not found.\n";
164 return false;
165 }
166
167 if (file->is_directory()) {
168 PT(VirtualFileMountSystem) new_mount =
169 new VirtualFileMountSystem(virtual_filename);
170 return mount(new_mount, mount_point, flags);
171
172 } else {
173 // It's not a directory; it must be a Multifile.
174 PT(Multifile) multifile = new Multifile;
175 multifile->set_encryption_password(password);
176
177 // For now these are always opened read only. Maybe later we'll support
178 // read-write on Multifiles.
179 flags |= MF_read_only;
180 if (!multifile->open_read(virtual_filename)) {
181 return false;
182 }
183
184 return mount(multifile, mount_point, flags);
185 }
186}
187
188/**
189 * Adds the given VirtualFileMount object to the mount list. This is a lower-
190 * level function than the other flavors of mount(); it requires you to create
191 * a VirtualFileMount object specifically.
192 */
194mount(VirtualFileMount *mount, const Filename &mount_point, int flags) {
195 if (express_cat->is_debug()) {
196 express_cat->debug()
197 << "mount " << *mount << " under " << mount_point << "\n";
198 }
199
200 _lock.lock();
201 bool result = do_mount(mount, mount_point, flags);
202 _lock.unlock();
203 return result;
204}
205
206/**
207 * Unmounts all appearances of the indicated Multifile from the file system.
208 * Returns the number of appearances unmounted.
209 */
211unmount(Multifile *multifile) {
212 _lock.lock();
213 Mounts::iterator ri, wi;
214 wi = ri = _mounts.begin();
215 while (ri != _mounts.end()) {
216 VirtualFileMount *mount = (*ri);
217 (*wi) = mount;
218
219 if (mount->is_exact_type(VirtualFileMountMultifile::get_class_type())) {
222 if (mmount->get_multifile() == multifile) {
223 // Remove this one. Don't increment wi.
224 if (express_cat->is_debug()) {
225 express_cat->debug()
226 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n";
227 }
228 mount->_file_system = nullptr;
229
230 } else {
231 // Don't remove this one.
232 ++wi;
233 }
234 } else {
235 // Don't remove this one.
236 ++wi;
237 }
238 ++ri;
239 }
240
241 int num_removed = _mounts.end() - wi;
242 _mounts.erase(wi, _mounts.end());
243 ++_mount_seq;
244 _lock.unlock();
245 return num_removed;
246}
247
248/**
249 * Unmounts all appearances of the indicated directory name or multifile name
250 * from the file system. Returns the number of appearances unmounted.
251 */
253unmount(const Filename &physical_filename) {
254 _lock.lock();
255 Mounts::iterator ri, wi;
256 wi = ri = _mounts.begin();
257 while (ri != _mounts.end()) {
258 VirtualFileMount *mount = (*ri);
259 (*wi) = mount;
260
261 if (mount->is_exact_type(VirtualFileMountSystem::get_class_type())) {
262 VirtualFileMountSystem *smount =
264 if (smount->get_physical_filename() == physical_filename) {
265 // Remove this one. Don't increment wi.
266 if (express_cat->is_debug()) {
267 express_cat->debug()
268 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n";
269 }
270 mount->_file_system = nullptr;
271
272 } else {
273 // Don't remove this one.
274 ++wi;
275 }
276
277 } else if (mount->is_exact_type(VirtualFileMountMultifile::get_class_type())) {
280 if (mmount->get_multifile()->get_multifile_name() == physical_filename) {
281 // Remove this one. Don't increment wi.
282 if (express_cat->is_debug()) {
283 express_cat->debug()
284 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n";
285 }
286 mount->_file_system = nullptr;
287
288 } else {
289 // Don't remove this one.
290 ++wi;
291 }
292
293 } else {
294 // Don't remove this one.
295 ++wi;
296 }
297 ++ri;
298 }
299
300 int num_removed = _mounts.end() - wi;
301 _mounts.erase(wi, _mounts.end());
302 ++_mount_seq;
303 _lock.unlock();
304 return num_removed;
305}
306
307/**
308 * Unmounts the indicated VirtualFileMount object from the file system.
309 * Returns the number of appearances unmounted.
310 */
313 _lock.lock();
314 Mounts::iterator ri, wi;
315 wi = ri = _mounts.begin();
316 while (ri != _mounts.end()) {
317 (*wi) = (*ri);
318 if ((*ri) == mount) {
319 // Remove this one. Don't increment wi.
320 if (express_cat->is_debug()) {
321 express_cat->debug()
322 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n";
323 }
324 (*ri)->_file_system = nullptr;
325
326 } else {
327 // Don't remove this one.
328 ++wi;
329 }
330 ++ri;
331 }
332
333 int num_removed = _mounts.end() - wi;
334 _mounts.erase(wi, _mounts.end());
335 ++_mount_seq;
336 _lock.unlock();
337 return num_removed;
338}
339
340/**
341 * Unmounts all systems attached to the given mount point from the file
342 * system. Returns the number of appearances unmounted.
343 */
345unmount_point(const Filename &mount_point) {
346 _lock.lock();
347 Filename nmp = normalize_mount_point(mount_point);
348 Mounts::iterator ri, wi;
349 wi = ri = _mounts.begin();
350 while (ri != _mounts.end()) {
351 VirtualFileMount *mount = (*ri);
352 (*wi) = mount;
353
354 if (mount->get_mount_point() == nmp) {
355 // Remove this one. Don't increment wi.
356 if (express_cat->is_debug()) {
357 express_cat->debug()
358 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n";
359 }
360 mount->_file_system = nullptr;
361
362 } else {
363 // Don't remove this one.
364 ++wi;
365 }
366 ++ri;
367 }
368
369 int num_removed = _mounts.end() - wi;
370 _mounts.erase(wi, _mounts.end());
371 ++_mount_seq;
372 _lock.unlock();
373 return num_removed;
374}
375
376/**
377 * Unmounts all files from the file system. Returns the number of systems
378 * unmounted.
379 */
381unmount_all() {
382 _lock.lock();
383 Mounts::iterator ri;
384 for (ri = _mounts.begin(); ri != _mounts.end(); ++ri) {
385 VirtualFileMount *mount = (*ri);
386 if (express_cat->is_debug()) {
387 express_cat->debug()
388 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n";
389 }
390 mount->_file_system = nullptr;
391 }
392
393 int num_removed = _mounts.size();
394 _mounts.clear();
395 ++_mount_seq;
396 _lock.unlock();
397 return num_removed;
398}
399
400/**
401 * Returns the number of individual mounts in the system.
402 */
404get_num_mounts() const {
405 _lock.lock();
406 int result = _mounts.size();
407 _lock.unlock();
408 return result;
409}
410
411/**
412 * Returns the nth mount in the system.
413 */
415get_mount(int n) const {
416 _lock.lock();
417 nassertd(n >= 0 && n < (int)_mounts.size()) {
418 _lock.unlock();
419 return nullptr;
420 }
421 PT(VirtualFileMount) result = _mounts[n];
422 _lock.unlock();
423 return result;
424}
425
426/**
427 * Changes the current directory. This is used to resolve relative pathnames
428 * in get_file() and/or find_file(). Returns true if successful, false
429 * otherwise.
430 */
432chdir(const Filename &new_directory) {
433 _lock.lock();
434 if (new_directory == "/") {
435 // We can always return to the root.
436 _cwd = new_directory;
437 _lock.unlock();
438 return true;
439 }
440
441 PT(VirtualFile) file = do_get_file(new_directory, OF_status_only);
442 if (file != nullptr && file->is_directory()) {
443 _cwd = file->get_filename();
444 _lock.unlock();
445 return true;
446 }
447 _lock.unlock();
448 return false;
449}
450
451/**
452 * Returns the current directory name. See chdir().
453 */
455get_cwd() const {
456 _lock.lock();
457 Filename result = _cwd;
458 _lock.unlock();
459 return result;
460}
461
462/**
463 * Attempts to create a directory within the file system. Returns true on
464 * success, false on failure (for instance, because the parent directory does
465 * not exist, or is read-only). If the directory already existed prior to
466 * this call, returns true.
467 */
469make_directory(const Filename &filename) {
470 _lock.lock();
471 PT(VirtualFile) result = do_get_file(filename, OF_make_directory);
472 _lock.unlock();
473 nassertr_always(result != nullptr, false);
474 return result->is_directory();
475}
476
477/**
478 * Attempts to create a directory within the file system. Will also create
479 * any intervening directories needed. Returns true on success, false on
480 * failure.
481 */
483make_directory_full(const Filename &filename) {
484 _lock.lock();
485
486 // First, make sure everything up to the last path is known. We don't care
487 // too much if any of these fail; maybe they failed because the directory
488 // was already there.
489 string dirname = filename;
490 size_t slash = dirname.find('/', 1);
491 while (slash != string::npos) {
492 Filename component(dirname.substr(0, slash));
493 do_get_file(component, OF_make_directory);
494 slash = dirname.find('/', slash + 1);
495 }
496
497 // Now make the last one, and check the return value.
498 PT(VirtualFile) result = do_get_file(filename, OF_make_directory);
499 _lock.unlock();
500 return (result != nullptr) ? result->is_directory() : false;
501}
502
503/**
504 * Looks up the file by the indicated name in the file system. Returns a
505 * VirtualFile pointer representing the file if it is found, or NULL if it is
506 * not.
507 *
508 * If status_only is true, the file will be checked for existence and length
509 * and so on, but the returned file's contents cannot be read. This is an
510 * optimization which is especially important for certain mount types, for
511 * instance HTTP, for which opening a file to determine its status is
512 * substantially less expensive than opening it to read its contents.
513 */
515get_file(const Filename &filename, bool status_only) const {
516 int open_flags = status_only ? OF_status_only : 0;
517 _lock.lock();
518 PT(VirtualFile) result = do_get_file(filename, open_flags);
519 _lock.unlock();
520 return result;
521}
522
523/**
524 * Attempts to create a file by the indicated name in the filesystem, if
525 * possible, and returns it. If a file by this name already exists, returns
526 * the same thing as get_file(). If the filename is located within a read-
527 * only directory, or the directory doesn't exist, returns NULL.
528 */
530create_file(const Filename &filename) {
531 _lock.lock();
532 PT(VirtualFile) result = do_get_file(filename, OF_create_file);
533 _lock.unlock();
534 return result;
535}
536
537/**
538 * Uses the indicated search path to find the file within the file system.
539 * Returns the first occurrence of the file found, or NULL if the file cannot
540 * be found.
541 */
543find_file(const Filename &filename, const DSearchPath &searchpath,
544 bool status_only) const {
545 if (!filename.is_local()) {
546 return get_file(filename, status_only);
547 }
548
549 int num_directories = searchpath.get_num_directories();
550 for (int i = 0; i < num_directories; ++i) {
551 Filename match(searchpath.get_directory(i), filename);
552 if (searchpath.get_directory(i) == "." &&
553 filename.is_fully_qualified()) {
554 // A special case for the "." directory: to avoid prefixing an endless
555 // stream of . in front of files, if the filename already has a .
556 // prefixed (i.e. is_fully_qualified() is true), we don't prefix
557 // another one.
558 match = filename;
559 }
560 PT(VirtualFile) found_file = get_file(match, status_only);
561 if (found_file != nullptr) {
562 return found_file;
563 }
564 }
565
566 return nullptr;
567}
568
569/**
570 * Attempts to delete the indicated file or directory. This can remove a
571 * single file or an empty directory. It will not remove a nonempty
572 * directory. Returns true on success, false on failure.
573 */
575delete_file(const Filename &filename) {
576 PT(VirtualFile) file = get_file(filename, true);
577 if (file == nullptr) {
578 return false;
579 }
580
581 return file->delete_file();
582}
583
584/**
585 * Attempts to move or rename the indicated file or directory. If the
586 * original file is an ordinary file, it will quietly replace any already-
587 * existing file in the new filename (but not a directory). If the original
588 * file is a directory, the new filename must not already exist.
589 *
590 * If the file is a directory, the new filename must be within the same mount
591 * point. If the file is an ordinary file, the new filename may be anywhere;
592 * but if it is not within the same mount point then the rename operation is
593 * automatically performed as a two-step copy-and-delete operation.
594 */
596rename_file(const Filename &orig_filename, const Filename &new_filename) {
597 _lock.lock();
598 PT(VirtualFile) orig_file = do_get_file(orig_filename, OF_status_only);
599 if (orig_file == nullptr) {
600 _lock.unlock();
601 return false;
602 }
603
604 PT(VirtualFile) new_file = do_get_file(new_filename, OF_status_only | OF_allow_nonexist);
605 if (new_file == nullptr) {
606 _lock.unlock();
607 return false;
608 }
609
610 _lock.unlock();
611
612 return orig_file->rename_file(new_file);
613}
614
615/**
616 * Attempts to copy the contents of the indicated file to the indicated file.
617 * Returns true on success, false on failure.
618 */
620copy_file(const Filename &orig_filename, const Filename &new_filename) {
621 PT(VirtualFile) orig_file = get_file(orig_filename, true);
622 if (orig_file == nullptr) {
623 return false;
624 }
625
626 PT(VirtualFile) new_file = create_file(new_filename);
627 if (new_file == nullptr) {
628 return false;
629 }
630
631 return orig_file->copy_file(new_file);
632}
633
634/**
635 * Searches the given search path for the filename. If it is found, updates
636 * the filename to the full pathname found and returns true; otherwise,
637 * returns false.
638 */
640resolve_filename(Filename &filename,
641 const DSearchPath &searchpath,
642 const string &default_extension) const {
643 PT(VirtualFile) found;
644
645 if (filename.is_local()) {
646 found = find_file(filename, searchpath, true);
647
648 if (found.is_null()) {
649 // We didn't find it with the given extension; can we try the default
650 // extension?
651 if (filename.get_extension().empty() && !default_extension.empty()) {
652 Filename try_ext = filename;
653 try_ext.set_extension(default_extension);
654 found = find_file(try_ext, searchpath, true);
655 }
656 }
657 } else {
658 if (exists(filename)) {
659 // The full pathname exists. Return true.
660 return true;
661 } else {
662 // The full pathname doesn't exist with the given extension; does it
663 // exist with the default extension?
664 if (filename.get_extension().empty() && !default_extension.empty()) {
665 Filename try_ext = filename;
666 try_ext.set_extension(default_extension);
667 found = get_file(try_ext, true);
668 }
669 }
670 }
671
672 if (!found.is_null()) {
673 filename = found->get_original_filename();
674 return true;
675 }
676
677 return false;
678}
679
680/**
681 * Searches all the directories in the search list for the indicated file, in
682 * order. Fills up the results list with *all* of the matching filenames
683 * found, if any. Returns the number of matches found.
684 *
685 * It is the responsibility of the the caller to clear the results list first;
686 * otherwise, the newly-found files will be appended to the list.
687 */
689find_all_files(const Filename &filename, const DSearchPath &searchpath,
690 DSearchPath::Results &results) const {
691 int num_added = 0;
692
693 if (filename.is_local()) {
694 int num_directories = searchpath.get_num_directories();
695 for (int i = 0; i < num_directories; ++i) {
696 Filename match(searchpath.get_directory(i), filename);
697 if (exists(match)) {
698 if (searchpath.get_directory(i) == "." &&
699 filename.is_fully_qualified()) {
700 // A special case for the "." directory: to avoid prefixing an
701 // endless stream of . in front of files, if the filename already
702 // has a . prefixed (i.e. is_fully_fully_qualified() is true), we
703 // don't prefix another one.
704 results.add_file(filename);
705 } else {
706 results.add_file(match);
707 }
708 ++num_added;
709 }
710 }
711 }
712
713 return num_added;
714}
715
716/**
717 * Print debugging information. (e.g. from Python or gdb prompt).
718 */
720write(ostream &out) const {
721 _lock.lock();
722 Mounts::const_iterator mi;
723 for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) {
724 VirtualFileMount *mount = (*mi);
725 mount->write(out);
726 }
727 _lock.unlock();
728}
729
730
731/**
732 * Returns the default global VirtualFileSystem. You may create your own
733 * personal VirtualFileSystem objects and use them for whatever you like, but
734 * Panda will attempt to load models and stuff from this default object.
735 *
736 * Initially, the global VirtualFileSystem is set up to mount the OS
737 * filesystem to root; i.e. it is equivalent to the OS filesystem. This may
738 * be subsequently adjusted by the user.
739 */
742 if (_global_ptr == nullptr) {
743 // Make sure this is initialized.
745
746 _global_ptr = new VirtualFileSystem;
747
748 // Set up the default mounts. First, there is always the root mount.
749 _global_ptr->mount("/", "/", 0);
750
751 // And our initial cwd comes from the environment.
752 _global_ptr->chdir(ExecutionEnvironment::get_cwd());
753
754 // Then, we add whatever mounts are listed in the Configrc file.
755 ConfigVariableList mounts
756 ("vfs-mount",
757 PRC_DESC("vfs-mount system-filename mount-point [options]"));
758
759 int num_unique_values = mounts.get_num_unique_values();
760 for (int i = 0; i < num_unique_values; i++) {
761 string mount_desc = mounts.get_unique_value(i);
762
763 // The vfs-mount syntax is:
764
765 // vfs-mount system-filename mount-point [options]
766
767 // The last two spaces mark the beginning of the mount point, and of the
768 // options, respectively. There might be multiple spaces in the system
769 // filename, which are part of the filename.
770
771 // The last space marks the beginning of the mount point. Spaces before
772 // that are part of the system filename.
773 size_t space = mount_desc.rfind(' ');
774 if (space == string::npos) {
775 express_cat.warning()
776 << "No space in vfs-mount descriptor: " << mount_desc << "\n";
777
778 } else {
779 string mount_point = mount_desc.substr(space + 1);
780 while (space > 0 && isspace(mount_desc[space - 1])) {
781 space--;
782 }
783 mount_desc = mount_desc.substr(0, space);
784 string options;
785
786 space = mount_desc.rfind(' ');
787 if (space != string::npos) {
788 // If there's another space, we have the optional options field.
789 options = mount_point;
790 mount_point = mount_desc.substr(space + 1);
791 while (space > 0 && isspace(mount_desc[space - 1])) {
792 --space;
793 }
794 mount_desc = mount_desc.substr(0, space);
795 }
796
797 mount_desc = ExecutionEnvironment::expand_string(mount_desc);
798 Filename physical_filename = Filename::from_os_specific(mount_desc);
799
800 int flags;
801 string password;
802 parse_options(options, flags, password);
803 _global_ptr->mount(physical_filename, mount_point, flags, password);
804 }
805 }
806
807 ConfigVariableString vfs_mount_ramdisk
808 ("vfs-mount-ramdisk", "",
809 PRC_DESC("vfs-mount-ramdisk mount-point [options]"));
810 if (!vfs_mount_ramdisk.empty()) {
811 string mount_point = vfs_mount_ramdisk;
812 string options;
813
814 size_t space = mount_point.rfind(' ');
815 if (space != string::npos) {
816 // If there's a space, we have the optional options field.
817 options = mount_point.substr(space + 1);
818 while (space > 0 && isspace(mount_point[space - 1])) {
819 --space;
820 }
821 mount_point = mount_point.substr(0, space);
822 }
823
824 int flags;
825 string password;
826 parse_options(options, flags, password);
827
829 _global_ptr->mount(ramdisk, mount_point, flags);
830 }
831 }
832
833 return _global_ptr;
834}
835
836/**
837 * Convenience function; returns a newly allocated istream if the file exists
838 * and can be read, or NULL otherwise. Does not return an invalid istream.
839 *
840 * If auto_unwrap is true, an explicitly-named .pz file is automatically
841 * decompressed and the decompressed contents are returned. This is different
842 * than vfs-implicit-pz, which will automatically decompress a file if the
843 * extension .pz is *not* given.
844 */
846open_read_file(const Filename &filename, bool auto_unwrap) const {
847 PT(VirtualFile) file = get_file(filename, false);
848 if (file == nullptr) {
849 return nullptr;
850 }
851 istream *str = file->open_read_file(auto_unwrap);
852 if (str != nullptr && str->fail()) {
853 close_read_file(str);
854 str = nullptr;
855 }
856 return str;
857}
858
859/**
860 * Closes a file opened by a previous call to open_read_file(). This really
861 * just deletes the istream pointer, but it is recommended to use this
862 * interface instead of deleting it explicitly, to help work around compiler
863 * issues.
864 */
866close_read_file(istream *stream) {
867 if (stream != nullptr) {
868 // For some reason--compiler bug in gcc 3.2?--explicitly deleting the
869 // stream pointer does not call the appropriate global delete function;
870 // instead apparently calling the system delete function. So we call the
871 // delete function by hand instead.
872#if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
873 stream->~istream();
874 (*global_operator_delete)(stream);
875#else
876 delete stream;
877#endif
878 }
879}
880
881/**
882 * Convenience function; returns a newly allocated ostream if the file exists
883 * and can be written, or NULL otherwise. Does not return an invalid ostream.
884 *
885 * If auto_wrap is true, an explicitly-named .pz file is automatically
886 * compressed while writing. If truncate is true, the file is truncated to
887 * zero length before writing.
888 */
890open_write_file(const Filename &filename, bool auto_wrap, bool truncate) {
891 PT(VirtualFile) file = create_file(filename);
892 if (file == nullptr) {
893 return nullptr;
894 }
895 ostream *str = file->open_write_file(auto_wrap, truncate);
896 if (str != nullptr && str->fail()) {
897 close_write_file(str);
898 str = nullptr;
899 }
900 return str;
901}
902
903/**
904 * Works like open_write_file(), but the file is opened in append mode. Like
905 * open_write_file, the returned pointer should eventually be passed to
906 * close_write_file().
907 */
909open_append_file(const Filename &filename) {
910 PT(VirtualFile) file = create_file(filename);
911 if (file == nullptr) {
912 return nullptr;
913 }
914 ostream *str = file->open_append_file();
915 if (str != nullptr && str->fail()) {
916 close_write_file(str);
917 str = nullptr;
918 }
919 return str;
920}
921
922/**
923 * Closes a file opened by a previous call to open_write_file(). This really
924 * just deletes the ostream pointer, but it is recommended to use this
925 * interface instead of deleting it explicitly, to help work around compiler
926 * issues.
927 */
929close_write_file(ostream *stream) {
930 if (stream != nullptr) {
931#if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
932 stream->~ostream();
933 (*global_operator_delete)(stream);
934#else
935 delete stream;
936#endif
937 }
938}
939
940/**
941 * Convenience function; returns a newly allocated iostream if the file exists
942 * and can be written, or NULL otherwise. Does not return an invalid
943 * iostream.
944 */
946open_read_write_file(const Filename &filename, bool truncate) {
947 PT(VirtualFile) file = create_file(filename);
948 if (file == nullptr) {
949 return nullptr;
950 }
951 iostream *str = file->open_read_write_file(truncate);
952 if (str != nullptr && str->fail()) {
954 str = nullptr;
955 }
956 return str;
957}
958
959/**
960 * Works like open_read_write_file(), but the file is opened in append mode.
961 * Like open_read_write_file, the returned pointer should eventually be passed
962 * to close_read_write_file().
963 */
965open_read_append_file(const Filename &filename) {
966 PT(VirtualFile) file = create_file(filename);
967 if (file == nullptr) {
968 return nullptr;
969 }
970 iostream *str = file->open_read_append_file();
971 if (str != nullptr && str->fail()) {
973 str = nullptr;
974 }
975 return str;
976}
977
978/**
979 * Closes a file opened by a previous call to open_read_write_file(). This
980 * really just deletes the iostream pointer, but it is recommended to use this
981 * interface instead of deleting it explicitly, to help work around compiler
982 * issues.
983 */
985close_read_write_file(iostream *stream) {
986 if (stream != nullptr) {
987#if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
988 stream->~iostream();
989 (*global_operator_delete)(stream);
990#else
991 delete stream;
992#endif
993 }
994}
995
996/**
997 * See Filename::atomic_compare_and_exchange_contents().
998 */
1000atomic_compare_and_exchange_contents(const Filename &filename, string &orig_contents,
1001 const string &old_contents,
1002 const string &new_contents) {
1003 PT(VirtualFile) file = create_file(filename);
1004 if (file == nullptr) {
1005 return false;
1006 }
1007
1008 return file->atomic_compare_and_exchange_contents(orig_contents, old_contents, new_contents);
1009}
1010
1011/**
1012 * See Filename::atomic_read_contents().
1013 */
1015atomic_read_contents(const Filename &filename, string &contents) const {
1016 PT(VirtualFile) file = get_file(filename, false);
1017 if (file == nullptr) {
1018 return false;
1019 }
1020
1021 return file->atomic_read_contents(contents);
1022}
1023
1024/**
1025 * Adds to names a list of all the mount points in use that are one directory
1026 * below path, if any. That is, these are the external files or directories
1027 * mounted directly to the indicated path.
1028 *
1029 * The names vector is filled with a set of basenames, the basename part of
1030 * the mount point.
1031 */
1033scan_mount_points(vector_string &names, const Filename &path) const {
1034 nassertv(!path.empty() && !path.is_local());
1035 string prefix = path.get_fullpath().substr(1);
1036 Mounts::const_iterator mi;
1037 for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) {
1038 VirtualFileMount *mount = (*mi);
1039
1040 string mount_point = mount->get_mount_point();
1041 if (prefix.empty()) {
1042 // The indicated path is the root. Is the mount point on the root?
1043 if (mount_point.find('/') == string::npos) {
1044 // No embedded slashes, so the mount point is only one directory below
1045 // the root.
1046 names.push_back(mount_point);
1047 }
1048 } else {
1049 if (mount_point.substr(0, prefix.length()) == prefix &&
1050 mount_point.length() > prefix.length() &&
1051 mount_point[prefix.length()] == '/') {
1052 // This mount point is below the indicated path. Is it only one
1053 // directory below?
1054 string basename = mount_point.substr(prefix.length());
1055 if (basename.find('/') == string::npos) {
1056 // No embedded slashes, so it's only one directory below.
1057 names.push_back(basename);
1058 }
1059 }
1060 }
1061 }
1062}
1063
1064
1065/**
1066 * Parses all of the option flags in the options list on the vfs-mount
1067 * Config.prc line.
1068 */
1070parse_options(const string &options, int &flags, string &password) {
1071 flags = 0;
1072 password = string();
1073
1074 // Split the options up by commas.
1075 size_t p = 0;
1076 size_t q = options.find(',', p);
1077 while (q != string::npos) {
1078 parse_option(options.substr(p, q - p),
1079 flags, password);
1080 p = q + 1;
1081 q = options.find(',', p);
1082 }
1083 parse_option(options.substr(p), flags, password);
1084}
1085
1086/**
1087 * Parses one of the option flags in the options list on the vfs-mount
1088 * Config.prc line.
1089 */
1091parse_option(const string &option, int &flags, string &password) {
1092 if (option == "0" || option.empty()) {
1093 // 0 is the null option.
1094 } else if (option == "ro") {
1095 flags |= MF_read_only;
1096 } else if (option.substr(0, 3) == "pw:") {
1097 password = option.substr(3);
1098 } else {
1099 express_cat.warning()
1100 << "Invalid option on vfs-mount: \"" << option << "\"\n";
1101 }
1102}
1103
1104/**
1105 * Converts the mount point string supplied by the user to standard form
1106 * (relative to the current directory, with no double slashes, and not
1107 * terminating with a slash). The initial slash is removed.
1108 *
1109 * Assumes the lock is already held.
1110 */
1111Filename VirtualFileSystem::
1112normalize_mount_point(const Filename &mount_point) const {
1113 Filename nmp = mount_point;
1114 if (nmp.is_local()) {
1115 nmp = Filename(_cwd, mount_point);
1116 }
1117 nmp.standardize();
1118 nassertr(!nmp.empty() && nmp[0] == '/', nmp);
1119 return nmp.get_fullpath().substr(1);
1120}
1121
1122/**
1123 * The private implementation of mount(). Assumes the lock is already held.
1124 */
1125bool VirtualFileSystem::
1126do_mount(VirtualFileMount *mount, const Filename &mount_point, int flags) {
1127 nassertr(mount->_file_system == nullptr, false);
1128 mount->_file_system = this;
1129 mount->_mount_point = normalize_mount_point(mount_point);
1130 mount->_mount_flags = flags;
1131 _mounts.push_back(mount);
1132 ++_mount_seq;
1133 return true;
1134}
1135
1136/**
1137 * The private implementation of get_file(), create_file(), and
1138 * make_directory(). Assumes the lock is already held.
1139 */
1140PT(VirtualFile) VirtualFileSystem::
1141do_get_file(const Filename &filename, int open_flags) const {
1142 if (filename.empty()) {
1143 return nullptr;
1144 }
1145 Filename pathname(filename);
1146 if (pathname.is_local()) {
1147 pathname = Filename(_cwd, filename);
1148 if (filename.is_text()) {
1149 pathname.set_text();
1150 }
1151 }
1152 pathname.standardize();
1153 Filename strpath = pathname.get_filename_index(0).get_fullpath().substr(1);
1154 strpath.set_type(filename.get_type());
1155 // Also transparently look for a regular file suffixed .pz.
1156 Filename strpath_pz = strpath + ".pz";
1157
1158 // Now scan all the mount points, from the back (since later mounts override
1159 // more recent ones), until a match is found.
1160 PT(VirtualFile) found_file = nullptr;
1161 VirtualFileComposite *composite_file = nullptr;
1162
1163 // We use an index instead of an iterator, since the vector might change if
1164 // implicit mounts are added during this loop.
1165 unsigned int start_seq = _mount_seq;
1166
1167 size_t i = _mounts.size();
1168 while (i > 0) {
1169 --i;
1170 VirtualFileMount *mount = _mounts[i];
1171 Filename mount_point = mount->get_mount_point();
1172 if (strpath == mount_point) {
1173 // Here's an exact match on the mount point. This filename is the root
1174 // directory of this mount object.
1175 if (consider_match(found_file, composite_file, mount, "", pathname,
1176 false, open_flags)) {
1177 return found_file;
1178 }
1179 } else if (mount_point.empty()) {
1180 // This is the root mount point; all files are in here.
1181 if (consider_match(found_file, composite_file, mount, strpath,
1182 pathname, false, open_flags)) {
1183 return found_file;
1184 }
1185#ifdef HAVE_ZLIB
1186 if (vfs_implicit_pz) {
1187 if (consider_match(found_file, composite_file, mount, strpath_pz,
1188 pathname, true, open_flags)) {
1189 return found_file;
1190 }
1191 }
1192#endif // HAVE_ZLIB
1193
1194 } else if (strpath.length() > mount_point.length() &&
1195 mount_point == strpath.substr(0, mount_point.length()) &&
1196 strpath[mount_point.length()] == '/') {
1197 // This pathname falls within this mount system.
1198 Filename local_filename = strpath.substr(mount_point.length() + 1);
1199 Filename local_filename_pz = strpath_pz.substr(mount_point.length() + 1);
1200 if (consider_match(found_file, composite_file, mount, local_filename,
1201 pathname, false, open_flags)) {
1202 return found_file;
1203 }
1204#ifdef HAVE_ZLIB
1205 if (vfs_implicit_pz) {
1206 // Bingo!
1207 if (consider_match(found_file, composite_file, mount, local_filename_pz,
1208 pathname, true, open_flags)) {
1209 return found_file;
1210 }
1211 }
1212#endif // HAVE_ZLIB
1213 }
1214
1215 // If we discover that a file has been implicitly mounted during one of
1216 // the above operations, start over from the beginning of the loop.
1217 if (start_seq != _mount_seq) {
1218 start_seq = _mount_seq;
1219 i = _mounts.size();
1220 }
1221 }
1222
1223 if (found_file == nullptr && vfs_implicit_mf) {
1224 // The file wasn't found, as-is. Does it appear to be an implicit .mf
1225 // file reference?
1226 ((VirtualFileSystem *)this)->consider_mount_mf(filename);
1227
1228 if (start_seq != _mount_seq) {
1229 // Yes, it was, or some nested file was. Now that we've implicitly
1230 // mounted the .mf file, go back and look again.
1231 return do_get_file(filename, open_flags);
1232 }
1233 }
1234
1235#if defined(_WIN32) && !defined(NDEBUG)
1236 if (!found_file) {
1237 // The file could not be found. Perhaps this is because the user passed
1238 // in a Windows-style path where a Unix-style path was expected?
1239 if (filename.length() > 2 && isalpha(filename[0]) && filename[1] == ':' &&
1240 (filename[2] == '\\' || filename[2] == '/')) {
1241
1242 Filename corrected_fn = Filename::from_os_specific(filename);
1243 if (corrected_fn.exists()) {
1244 express_cat.warning()
1245 << "Filename uses Windows-style path: " << filename << "\n";
1246 express_cat.warning()
1247 << " expected Unix-style path: " << corrected_fn << "\n";
1248 }
1249 }
1250 }
1251#endif
1252
1253 return found_file;
1254}
1255
1256/**
1257 * Evaluates one possible filename match found during a get_file() operation.
1258 * There may be multiple matches for a particular filename due to the
1259 * ambiguities introduced by allowing multiple mount points, so we may have to
1260 * keep searching even after the first match is found.
1261 *
1262 * Returns true if the search should terminate now, or false if it should keep
1263 * iterating.
1264 */
1265bool VirtualFileSystem::
1266consider_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_file,
1267 VirtualFileMount *mount, const Filename &local_filename,
1268 const Filename &original_filename, bool implicit_pz_file,
1269 int open_flags) const {
1270 PT(VirtualFile) vfile =
1271 mount->make_virtual_file(local_filename, original_filename, false, open_flags);
1272 if (!vfile->has_file() && ((open_flags & OF_allow_nonexist) == 0)) {
1273 // Keep looking.
1274 return false;
1275 }
1276
1277 if (found_file == nullptr) {
1278 // This was our first match. Save it.
1279 found_file = vfile;
1280 if (!found_file->is_directory() || ((open_flags & OF_make_directory) != 0)) {
1281 // If it's not a directory (or we wanted to make a directory), we're
1282 // done.
1283 return true;
1284 }
1285 // It is a directory, so save it for later.
1286 if (implicit_pz_file) {
1287 // Don't look for directories named file.pz.
1288 found_file = nullptr;
1289 }
1290
1291 } else {
1292 // This was our second match. The previous match(es) must have been
1293 // directories.
1294 if (!vfile->is_directory()) {
1295 // However, this one isn't a directory. We're done.
1296 return true;
1297 }
1298
1299 if (!implicit_pz_file) {
1300 // At least two directories matched to the same path. We need a
1301 // composite directory.
1302 if (composite_file == nullptr) {
1303 composite_file =
1305 composite_file->set_original_filename(original_filename);
1306 composite_file->add_component(found_file);
1307 found_file = composite_file;
1308 }
1309
1310 composite_file->add_component(vfile);
1311 }
1312 }
1313
1314 // Keep going, looking for more directories.
1315 return false;
1316}
1317
1318/**
1319 * The indicated filename was not found. Check to see if it is using an
1320 * implicit reference to a .mf file as a directory, that hasn't already been
1321 * mounted. If it is, mount the .mf file in-place, and return true; if it is
1322 * not, or if its .mf file is already mounted in-place, return false.
1323 *
1324 * Assumes the lock is already held.
1325 */
1326bool VirtualFileSystem::
1327consider_mount_mf(const Filename &filename) {
1328 Filename dirname = filename.get_dirname();
1329 if (dirname.empty() || dirname == filename) {
1330 // Reached the top directory; no .mf file references.
1331 return false;
1332 }
1333 if (is_directory(dirname)) {
1334 // Reached a real (or already-mounted) directory; no unmounted .mf file
1335 // references.
1336 return false;
1337 }
1338 if (dirname.get_extension() == "mf") {
1339 // Hey, here's a multifile reference!
1340 dirname.set_binary();
1341 PT(VirtualFile) file = do_get_file(dirname, false);
1342 if (file == nullptr || !file->is_regular_file()) {
1343 // Oh, never mind. Not a real file.
1344 return false;
1345 }
1346
1347 PT(Multifile) multifile = new Multifile;
1348
1349 istream *stream = file->open_read_file(false);
1350 if (stream == nullptr) {
1351 // Couldn't read file.
1352 return false;
1353 }
1354
1355 // Wrap a thread-safe wrapper around that stream, so multiple threads can
1356 // safely read the multifile simultaneously.
1357 IStreamWrapper *streamw = new IStreamWrapper(stream, true);
1358
1359 if (!multifile->open_read(streamw, true)) {
1360 // Invalid multifile.
1361 return false;
1362 }
1363
1364 multifile->set_multifile_name(dirname.get_basename());
1365 express_cat->info()
1366 << "Implicitly mounting " << dirname << "\n";
1367
1368 PT(VirtualFileMountMultifile) new_mount =
1369 new VirtualFileMountMultifile(multifile);
1370 return do_mount(new_mount, dirname, MF_read_only);
1371 }
1372
1373 // Recurse.
1374 return consider_mount_mf(dirname);
1375}
This class is similar to ConfigVariable, but it reports its value as a list of strings.
std::string get_unique_value(size_t n) const
Returns the nth unique value of the variable.
size_t get_num_unique_values() const
Returns the number of unique values in the variable.
This is a convenience class to specialize ConfigVariable as a string type.
void add_file(const Filename &file)
Adds a new file to the result list.
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition dSearchPath.h:28
get_num_directories
Returns the number of directories on the search list.
Definition dSearchPath.h:76
get_directory
Returns the nth directory on the search list.
Definition dSearchPath.h:76
static std::string expand_string(const std::string &str)
Reads the string, looking for environment variable names marked by a $.
get_cwd
Returns the name of the current working directory.
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
void set_type(Type type)
Sets the type of the file represented by the filename.
Definition filename.I:468
bool is_fully_qualified() const
Returns true if the filename is fully qualified, e.g.
Definition filename.I:562
std::string get_basename() const
Returns the basename part of the filename.
Definition filename.I:367
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 ...
Definition filename.cxx:836
bool is_text() const
Returns true if the Filename has been indicated to represent a text file via a previous call to set_t...
Definition filename.I:456
void standardize()
Converts the filename to standard form by replacing consecutive slashes with a single slash,...
Definition filename.cxx:900
void set_binary()
Indicates that the filename represents a binary file.
Definition filename.I:414
std::string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition filename.I:338
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
Definition filename.cxx:328
void set_extension(const std::string &s)
Replaces the file extension.
Definition filename.cxx:804
bool is_directory() const
Returns true if the filename exists on the physical disk and is a directory name, false otherwise.
std::string get_extension() const
Returns the file extension.
Definition filename.I:400
bool is_local() const
Returns true if the filename is local, e.g.
Definition filename.I:549
std::string get_dirname() const
Returns the directory part of the filename.
Definition filename.I:358
Type get_type() const
Returns the type of the file represented by the filename, as previously set by set_type().
Definition filename.I:485
bool exists() const
Returns true if the filename exists on the physical disk, false otherwise.
This class provides a locking wrapper around an arbitrary istream pointer.
A file that contains a set of files.
Definition multifile.h:37
const Filename & get_multifile_name() const
Returns the filename of the Multifile, if it is available.
Definition multifile.I:18
void set_encryption_password(const std::string &encryption_password)
Specifies the password that will be used to encrypt subfiles subsequently added to the multifile,...
Definition multifile.I:157
A composite directory within the VirtualFileSystem: this maps to more than one directory on different...
void add_component(VirtualFile *file)
Adds one more component to the composite directory.
Maps a Multifile's contents into the VirtualFileSystem.
Multifile * get_multifile() const
Returns the Multifile pointer that this mount object is based on.
Simulates an actual directory on disk with in-memory storage.
Maps an actual OS directory into the VirtualFileSystem.
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.
The abstract base class for a mount definition used within a VirtualFileSystem.
const Filename & get_mount_point() const
Returns the name of the directory within the virtual file system that this mount object is attached t...
A hierarchy of directories and files that appears to be one continuous file system,...
bool rename_file(const Filename &orig_filename, const Filename &new_filename)
Attempts to move or rename the indicated file or directory.
static void close_write_file(std::ostream *stream)
Closes a file opened by a previous call to open_write_file().
std::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,...
int unmount_point(const Filename &mount_point)
Unmounts all systems attached to the given mount point from the file system.
Filename get_cwd() const
Returns the current directory name.
void write(std::ostream &out) const
Print debugging information.
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists in the virtual file system hierarchy.
int unmount(Multifile *multifile)
Unmounts all appearances of the indicated Multifile from the file system.
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.
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 resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
std::ostream * open_append_file(const Filename &filename)
Works like open_write_file(), but the file is opened in append mode.
std::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,...
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
static void close_read_write_file(std::iostream *stream)
Closes a file opened by a previous call to open_read_write_file().
static void parse_options(const std::string &options, int &flags, std::string &password)
Parses all of the option flags in the options list on the vfs-mount Config.prc line.
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 parse_option(const std::string &option, int &flags, std::string &password)
Parses one of the option flags in the options list on the vfs-mount Config.prc line.
std::iostream * open_read_append_file(const Filename &filename)
Works like open_read_write_file(), but the file is opened in append mode.
bool mount_loop(const Filename &virtual_filename, const Filename &mount_point, int flags, const std::string &password="")
This is similar to mount(), but it receives the name of a Multifile that already appears within the v...
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,...
get_mount
Returns the nth mount in the system.
bool is_directory(const Filename &filename) const
Convenience function; returns true if the named file exists as a directory in the virtual file system...
bool chdir(const Filename &new_directory)
Changes the current directory.
get_num_mounts
Returns the number of individual mounts in the system.
bool atomic_compare_and_exchange_contents(const Filename &filename, std::string &orig_contents, const std::string &old_contents, const std::string &new_contents)
See Filename::atomic_compare_and_exchange_contents().
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
std::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,...
bool delete_file(const Filename &filename)
Attempts to delete the indicated file or directory.
bool make_directory_full(const Filename &filename)
Attempts to create a directory within the file system.
bool make_directory(const Filename &filename)
Attempts to create a directory within the file system.
bool mount(Multifile *multifile, const Filename &mount_point, int flags)
Mounts the indicated Multifile at the given mount point.
bool copy_file(const Filename &orig_filename, const Filename &new_filename)
Attempts to copy the contents of the indicated file to the indicated file.
int unmount_all()
Unmounts all files from the file system.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
bool atomic_read_contents(const Filename &filename, std::string &contents) const
See Filename::atomic_read_contents().
The abstract base class for a file or directory within the VirtualFileSystem.
Definition virtualFile.h:35
const Filename & get_original_filename() const
Returns the original filename as it was used to locate this VirtualFile.
Definition virtualFile.I:27
virtual bool is_directory() const
Returns true if this file represents a directory (and scan_directory() may be called),...
void set_original_filename(const Filename &filename)
Stores the original filename that was used to locate this VirtualFile.
Definition virtualFile.I:55
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void init_libexpress()
Initializes the library.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.