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 }
VirtualFileSystem::open_append_file
std::ostream * open_append_file(const Filename &filename)
Works like open_write_file(), but the file is opened in append mode.
Definition: virtualFileSystem.cxx:909
VirtualFileMountMultifile
Maps a Multifile's contents into the VirtualFileSystem.
Definition: virtualFileMountMultifile.h:26
ExecutionEnvironment::get_cwd
get_cwd
Returns the name of the current working directory.
Definition: executionEnvironment.h:61
VirtualFileSystem::get_cwd
Filename get_cwd() const
Returns the current directory name.
Definition: virtualFileSystem.cxx:455
IStreamWrapper
This class provides a locking wrapper around an arbitrary istream pointer.
Definition: streamWrapper.h:59
VirtualFileSystem::get_mount
get_mount
Returns the nth mount in the system.
Definition: virtualFileSystem.h:63
VirtualFileSystem::unmount
int unmount(Multifile *multifile)
Unmounts all appearances of the indicated Multifile from the file system.
Definition: virtualFileSystem.cxx:211
VirtualFileMountSystem
Maps an actual OS directory into the VirtualFileSystem.
Definition: virtualFileMountSystem.h:24
VirtualFileSystem::unmount_all
int unmount_all()
Unmounts all files from the file system.
Definition: virtualFileSystem.cxx:381
VirtualFileMountMultifile::get_multifile
Multifile * get_multifile() const
Returns the Multifile pointer that this mount object is based on.
Definition: virtualFileMountMultifile.I:27
VirtualFileSystem::create_file
PointerTo< VirtualFile > create_file(const Filename &filename)
Attempts to create a file by the indicated name in the filesystem, if possible, and returns it.
Definition: virtualFileSystem.cxx:530
Filename::get_dirname
std::string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:358
Filename::from_os_specific
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
VirtualFileSystem::parse_options
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.
Definition: virtualFileSystem.cxx:1070
ConfigVariableList::get_unique_value
std::string get_unique_value(size_t n) const
Returns the nth unique value of the variable.
Definition: configVariableList.I:77
VirtualFileComposite
A composite directory within the VirtualFileSystem: this maps to more than one directory on different...
Definition: virtualFileComposite.h:26
VirtualFileSystem::scan_mount_points
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,...
Definition: virtualFileSystem.cxx:1033
VirtualFileSystem::is_directory
bool is_directory(const Filename &filename) const
Convenience function; returns true if the named file exists and is a directory.
Definition: virtualFileSystem.I:27
Filename::exists
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1267
VirtualFileSystem::get_file
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
Definition: virtualFileSystem.cxx:515
VirtualFileSystem::get_num_mounts
get_num_mounts
Returns the number of individual mounts in the system.
Definition: virtualFileSystem.h:63
dcast.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VirtualFileSystem::make_directory
bool make_directory(const Filename &filename)
Attempts to create a directory within the file system.
Definition: virtualFileSystem.cxx:469
DSearchPath::Results
Definition: dSearchPath.h:30
VirtualFileSystem::write
void write(std::ostream &out) const
Print debugging information.
Definition: virtualFileSystem.cxx:720
virtualFileMountRamdisk.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Filename::get_filename_index
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
VirtualFileSystem::open_read_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,...
Definition: virtualFileSystem.cxx:946
virtualFileComposite.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VirtualFileSystem::exists
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists.
Definition: virtualFileSystem.I:18
VirtualFileSystem::unmount_point
int unmount_point(const Filename &mount_point)
Unmounts all systems attached to the given mount point from the file system.
Definition: virtualFileSystem.cxx:345
VirtualFile::set_original_filename
void set_original_filename(const Filename &filename)
Stores the original filename that was used to locate this VirtualFile.
Definition: virtualFile.I:55
VirtualFileSystem::find_all_files
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.
Definition: virtualFileSystem.cxx:689
VirtualFileSystem::close_read_write_file
static void close_read_write_file(std::iostream *stream)
Closes a file opened by a previous call to open_read_write_file().
Definition: virtualFileSystem.cxx:985
VirtualFileComposite::add_component
void add_component(VirtualFile *file)
Adds one more component to the composite directory.
Definition: virtualFileComposite.I:29
DSearchPath::Results::add_file
void add_file(const Filename &file)
Adds a new file to the result list.
Definition: dSearchPath.cxx:83
Filename::standardize
void standardize()
Converts the filename to standard form by replacing consecutive slashes with a single slash,...
Definition: filename.cxx:900
VirtualFileSystem::close_write_file
static void close_write_file(std::ostream *stream)
Closes a file opened by a previous call to open_write_file().
Definition: virtualFileSystem.cxx:929
VirtualFileSystem::atomic_read_contents
bool atomic_read_contents(const Filename &filename, std::string &contents) const
See Filename::atomic_read_contents().
Definition: virtualFileSystem.cxx:1015
VirtualFileSystem::mount_loop
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...
Definition: virtualFileSystem.cxx:158
Filename::is_text
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
VirtualFile::is_directory
virtual bool is_directory() const
Returns true if this file represents a directory (and scan_directory() may be called),...
Definition: virtualFile.cxx:41
VirtualFileSystem::open_write_file
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,...
Definition: virtualFileSystem.cxx:890
Filename::set_type
void set_type(Type type)
Sets the type of the file represented by the filename.
Definition: filename.I:468
VirtualFileSystem::make_directory_full
bool make_directory_full(const Filename &filename)
Attempts to create a directory within the file system.
Definition: virtualFileSystem.cxx:483
VirtualFileSystem::atomic_compare_and_exchange_contents
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().
Definition: virtualFileSystem.cxx:1000
virtualFileSimple.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
DSearchPath
This class stores a list of directories that can be searched, in order, to locate a particular file.
Definition: dSearchPath.h:28
VirtualFileSystem
A hierarchy of directories and files that appears to be one continuous file system,...
Definition: virtualFileSystem.h:40
Multifile
A file that contains a set of files.
Definition: multifile.h:37
DSearchPath::get_num_directories
get_num_directories
Returns the number of directories on the search list.
Definition: dSearchPath.h:76
VirtualFile::get_original_filename
const Filename & get_original_filename() const
Returns the original filename as it was used to locate this VirtualFile.
Definition: virtualFile.I:27
ExecutionEnvironment::expand_string
static std::string expand_string(const std::string &str)
Reads the string, looking for environment variable names marked by a $.
Definition: executionEnvironment.cxx:112
Multifile::get_multifile_name
const Filename & get_multifile_name() const
Returns the filename of the Multifile, if it is available.
Definition: multifile.I:18
Filename::get_fullpath
std::string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition: filename.I:338
VirtualFileSystem::find_file
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.
Definition: virtualFileSystem.cxx:543
Filename::is_local
bool is_local() const
Returns true if the filename is local, e.g.
Definition: filename.I:549
configVariableList.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VirtualFileMountRamdisk
Simulates an actual directory on disk with in-memory storage.
Definition: virtualFileMountRamdisk.h:30
Filename::get_type
Type get_type() const
Returns the type of the file represented by the filename, as previously set by set_type().
Definition: filename.I:485
Filename::is_directory
bool is_directory() const
Returns true if the filename exists and is a directory name, false otherwise.
Definition: filename.cxx:1359
virtualFileMountMultifile.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ConfigVariableList::get_num_unique_values
size_t get_num_unique_values() const
Returns the number of unique values in the variable.
Definition: configVariableList.I:68
executionEnvironment.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VirtualFileSystem::delete_file
bool delete_file(const Filename &filename)
Attempts to delete the indicated file or directory.
Definition: virtualFileSystem.cxx:575
VirtualFileSystem::mount
bool mount(Multifile *multifile, const Filename &mount_point, int flags)
Mounts the indicated Multifile at the given mount point.
Definition: virtualFileSystem.cxx:90
ConfigVariableList
This class is similar to ConfigVariable, but it reports its value as a list of strings.
Definition: configVariableList.h:31
VirtualFileSystem::get_global_ptr
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
Definition: virtualFileSystem.cxx:741
virtualFileSystem.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VirtualFileMount
The abstract base class for a mount definition used within a VirtualFileSystem.
Definition: virtualFileMount.h:31
virtualFileMount.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VirtualFileSystem::close_read_file
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
Definition: virtualFileSystem.cxx:866
VirtualFileSystem::copy_file
bool copy_file(const Filename &orig_filename, const Filename &new_filename)
Attempts to copy the contents of the indicated file to the indicated file.
Definition: virtualFileSystem.cxx:620
VirtualFile
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
pset.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Filename::set_extension
void set_extension(const std::string &s)
Replaces the file extension.
Definition: filename.cxx:804
DSearchPath::get_directory
get_directory
Returns the nth directory on the search list.
Definition: dSearchPath.h:76
virtualFileMountSystem.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
init_libexpress
void init_libexpress()
Initializes the library.
Definition: config_express.cxx:96
VirtualFileSystem::parse_option
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.
Definition: virtualFileSystem.cxx:1091
VirtualFileSystem::resolve_filename
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
Definition: virtualFileSystem.cxx:640
VirtualFileSystem::open_read_file
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,...
Definition: virtualFileSystem.cxx:846
dSearchPath.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VirtualFileSystem::open_read_append_file
std::iostream * open_read_append_file(const Filename &filename)
Works like open_read_write_file(), but the file is opened in append mode.
Definition: virtualFileSystem.cxx:965
Multifile::set_encryption_password
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:146
streamWrapper.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
configVariableString.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_express.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
VirtualFileSystem::rename_file
bool rename_file(const Filename &orig_filename, const Filename &new_filename)
Attempts to move or rename the indicated file or directory.
Definition: virtualFileSystem.cxx:596
Filename::get_extension
std::string get_extension() const
Returns the file extension.
Definition: filename.I:400
Filename::get_basename
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
Filename::set_binary
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:414
VirtualFileMountSystem::get_physical_filename
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.
Definition: virtualFileMountSystem.I:28
ConfigVariableString
This is a convenience class to specialize ConfigVariable as a string type.
Definition: configVariableString.h:23
VirtualFileSystem::chdir
bool chdir(const Filename &new_directory)
Changes the current directory.
Definition: virtualFileSystem.cxx:432
Filename::is_fully_qualified
bool is_fully_qualified() const
Returns true if the filename is fully qualified, e.g.
Definition: filename.I:562