Panda3D
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"
16 #include "virtualFileComposite.h"
17 #include "virtualFileMount.h"
20 #include "virtualFileMountSystem.h"
21 #include "streamWrapper.h"
22 #include "dSearchPath.h"
23 #include "dcast.h"
24 #include "config_express.h"
25 #include "configVariableList.h"
26 #include "configVariableString.h"
27 #include "executionEnvironment.h"
28 #include "pset.h"
29 
30 using std::iostream;
31 using std::istream;
32 using std::ostream;
33 using std::string;
34 
35 VirtualFileSystem *VirtualFileSystem::_global_ptr = nullptr;
36 
37 
38 /**
39  *
40  */
41 VirtualFileSystem::
42 VirtualFileSystem() :
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  */
81 VirtualFileSystem::
82 ~VirtualFileSystem() {
83  unmount_all();
84 }
85 
86 /**
87  * Mounts the indicated Multifile at the given mount point.
88  */
90 mount(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  */
117 mount(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  */
158 mount_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  */
194 mount(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  */
211 unmount(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())) {
220  VirtualFileMountMultifile *mmount =
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  */
253 unmount(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())) {
278  VirtualFileMountMultifile *mmount =
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  */
312 unmount(VirtualFileMount *mount) {
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  */
345 unmount_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  */
381 unmount_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  */
404 get_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  */
415 get_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  */
432 chdir(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  */
455 get_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  */
469 make_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  */
483 make_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  */
515 get_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  */
530 create_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  */
543 find_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  */
575 delete_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  */
596 rename_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  */
620 copy_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  */
640 resolve_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  */
689 find_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  */
720 write(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  */
741 get_global_ptr() {
742  if (_global_ptr == nullptr) {
743  // Make sure this is initialized.
744  init_libexpress();
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 
828  PT(VirtualFileMount) ramdisk = new VirtualFileMountRamdisk;
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  */
846 open_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  */
866 close_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  */
890 open_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  */
909 open_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  */
929 close_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  */
946 open_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  */
965 open_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  */
985 close_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  */
1000 atomic_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  */
1015 atomic_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  */
1033 scan_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  */
1070 parse_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  */
1091 parse_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  */
1111 Filename VirtualFileSystem::
1112 normalize_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  */
1125 bool VirtualFileSystem::
1126 do_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  */
1140 PT(VirtualFile) VirtualFileSystem::
1141 do_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  */
1265 bool VirtualFileSystem::
1266 consider_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 =
1304  new VirtualFileComposite((VirtualFileSystem *)this, found_file->get_original_filename());
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  */
1326 bool VirtualFileSystem::
1327 consider_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.
Definition: dSearchPath.cxx:83
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:39
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 and is a directory name, false otherwise.
Definition: filename.cxx:1359
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 disk, false otherwise.
Definition: filename.cxx:1267
This class provides a locking wrapper around an arbitrary istream pointer.
Definition: streamWrapper.h:59
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.
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.
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 and is a directory.
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),...
Definition: virtualFile.cxx:41
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.