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 that 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  */
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  */
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  */
403 int VirtualFileSystem::
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  nassertr_always(result != nullptr, false);
501  return result->is_directory();
502 }
503 
504 /**
505  * Looks up the file by the indicated name in the file system. Returns a
506  * VirtualFile pointer representing the file if it is found, or NULL if it is
507  * not.
508  *
509  * If status_only is true, the file will be checked for existence and length
510  * and so on, but the returned file's contents cannot be read. This is an
511  * optimization which is especially important for certain mount types, for
512  * instance HTTP, for which opening a file to determine its status is
513  * substantially less expensive than opening it to read its contents.
514  */
516 get_file(const Filename &filename, bool status_only) const {
517  int open_flags = status_only ? OF_status_only : 0;
518  _lock.lock();
519  PT(VirtualFile) result = do_get_file(filename, open_flags);
520  _lock.unlock();
521  return result;
522 }
523 
524 /**
525  * Attempts to create a file by the indicated name in the filesystem, if
526  * possible, and returns it. If a file by this name already exists, returns
527  * the same thing as get_file(). If the filename is located within a read-
528  * only directory, or the directory doesn't exist, returns NULL.
529  */
531 create_file(const Filename &filename) {
532  _lock.lock();
533  PT(VirtualFile) result = do_get_file(filename, OF_create_file);
534  _lock.unlock();
535  return result;
536 }
537 
538 /**
539  * Uses the indicated search path to find the file within the file system.
540  * Returns the first occurrence of the file found, or NULL if the file cannot
541  * be found.
542  */
544 find_file(const Filename &filename, const DSearchPath &searchpath,
545  bool status_only) const {
546  if (!filename.is_local()) {
547  return get_file(filename, status_only);
548  }
549 
550  int num_directories = searchpath.get_num_directories();
551  for (int i = 0; i < num_directories; ++i) {
552  Filename match(searchpath.get_directory(i), filename);
553  if (searchpath.get_directory(i) == "." &&
554  filename.is_fully_qualified()) {
555  // A special case for the "." directory: to avoid prefixing an endless
556  // stream of . in front of files, if the filename already has a .
557  // prefixed (i.e. is_fully_qualified() is true), we don't prefix
558  // another one.
559  match = filename;
560  }
561  PT(VirtualFile) found_file = get_file(match, status_only);
562  if (found_file != nullptr) {
563  return found_file;
564  }
565  }
566 
567  return nullptr;
568 }
569 
570 /**
571  * Attempts to delete the indicated file or directory. This can remove a
572  * single file or an empty directory. It will not remove a nonempty
573  * directory. Returns true on success, false on failure.
574  */
576 delete_file(const Filename &filename) {
577  PT(VirtualFile) file = get_file(filename, true);
578  if (file == nullptr) {
579  return false;
580  }
581 
582  return file->delete_file();
583 }
584 
585 /**
586  * Attempts to move or rename the indicated file or directory. If the
587  * original file is an ordinary file, it will quietly replace any already-
588  * existing file in the new filename (but not a directory). If the original
589  * file is a directory, the new filename must not already exist.
590  *
591  * If the file is a directory, the new filename must be within the same mount
592  * point. If the file is an ordinary file, the new filename may be anywhere;
593  * but if it is not within the same mount point then the rename operation is
594  * automatically performed as a two-step copy-and-delete operation.
595  */
597 rename_file(const Filename &orig_filename, const Filename &new_filename) {
598  _lock.lock();
599  PT(VirtualFile) orig_file = do_get_file(orig_filename, OF_status_only);
600  if (orig_file == nullptr) {
601  _lock.unlock();
602  return false;
603  }
604 
605  PT(VirtualFile) new_file = do_get_file(new_filename, OF_status_only | OF_allow_nonexist);
606  if (new_file == nullptr) {
607  _lock.unlock();
608  return false;
609  }
610 
611  _lock.unlock();
612 
613  return orig_file->rename_file(new_file);
614 }
615 
616 /**
617  * Attempts to copy the contents of the indicated file to the indicated file.
618  * Returns true on success, false on failure.
619  */
621 copy_file(const Filename &orig_filename, const Filename &new_filename) {
622  PT(VirtualFile) orig_file = get_file(orig_filename, true);
623  if (orig_file == nullptr) {
624  return false;
625  }
626 
627  PT(VirtualFile) new_file = create_file(new_filename);
628  if (new_file == nullptr) {
629  return false;
630  }
631 
632  return orig_file->copy_file(new_file);
633 }
634 
635 /**
636  * Searches the given search path for the filename. If it is found, updates
637  * the filename to the full pathname found and returns true; otherwise,
638  * returns false.
639  */
642  const DSearchPath &searchpath,
643  const string &default_extension) const {
644  PT(VirtualFile) found;
645 
646  if (filename.is_local()) {
647  found = find_file(filename, searchpath, true);
648 
649  if (found.is_null()) {
650  // We didn't find it with the given extension; can we try the default
651  // extension?
652  if (filename.get_extension().empty() && !default_extension.empty()) {
653  Filename try_ext = filename;
654  try_ext.set_extension(default_extension);
655  found = find_file(try_ext, searchpath, true);
656  }
657  }
658  } else {
659  if (exists(filename)) {
660  // The full pathname exists. Return true.
661  return true;
662  } else {
663  // The full pathname doesn't exist with the given extension; does it
664  // exist with the default extension?
665  if (filename.get_extension().empty() && !default_extension.empty()) {
666  Filename try_ext = filename;
667  try_ext.set_extension(default_extension);
668  found = get_file(try_ext, true);
669  }
670  }
671  }
672 
673  if (!found.is_null()) {
674  filename = found->get_original_filename();
675  return true;
676  }
677 
678  return false;
679 }
680 
681 /**
682  * Searches all the directories in the search list for the indicated file, in
683  * order. Fills up the results list with *all* of the matching filenames
684  * found, if any. Returns the number of matches found.
685  *
686  * It is the responsibility of the the caller to clear the results list first;
687  * otherwise, the newly-found files will be appended to the list.
688  */
690 find_all_files(const Filename &filename, const DSearchPath &searchpath,
691  DSearchPath::Results &results) const {
692  int num_added = 0;
693 
694  if (filename.is_local()) {
695  int num_directories = searchpath.get_num_directories();
696  for (int i = 0; i < num_directories; ++i) {
697  Filename match(searchpath.get_directory(i), filename);
698  if (exists(match)) {
699  if (searchpath.get_directory(i) == "." &&
700  filename.is_fully_qualified()) {
701  // A special case for the "." directory: to avoid prefixing an
702  // endless stream of . in front of files, if the filename already
703  // has a . prefixed (i.e. is_fully_fully_qualified() is true), we
704  // don't prefix another one.
705  results.add_file(filename);
706  } else {
707  results.add_file(match);
708  }
709  ++num_added;
710  }
711  }
712  }
713 
714  return num_added;
715 }
716 
717 /**
718  * Print debugging information. (e.g. from Python or gdb prompt).
719  */
721 write(ostream &out) const {
722  _lock.lock();
723  Mounts::const_iterator mi;
724  for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) {
725  VirtualFileMount *mount = (*mi);
726  mount->write(out);
727  }
728  _lock.unlock();
729 }
730 
731 
732 /**
733  * Returns the default global VirtualFileSystem. You may create your own
734  * personal VirtualFileSystem objects and use them for whatever you like, but
735  * Panda will attempt to load models and stuff from this default object.
736  *
737  * Initially, the global VirtualFileSystem is set up to mount the OS
738  * filesystem to root; i.e. it is equivalent to the OS filesystem. This may
739  * be subsequently adjusted by the user.
740  */
743  if (_global_ptr == nullptr) {
744  // Make sure this is initialized.
745  init_libexpress();
746 
747  _global_ptr = new VirtualFileSystem;
748 
749  // Set up the default mounts. First, there is always the root mount.
750  _global_ptr->mount("/", "/", 0);
751 
752  // And our initial cwd comes from the environment.
753  _global_ptr->chdir(ExecutionEnvironment::get_cwd());
754 
755  // Then, we add whatever mounts are listed in the Configrc file.
756  ConfigVariableList mounts
757  ("vfs-mount",
758  PRC_DESC("vfs-mount system-filename mount-point [options]"));
759 
760  int num_unique_values = mounts.get_num_unique_values();
761  for (int i = 0; i < num_unique_values; i++) {
762  string mount_desc = mounts.get_unique_value(i);
763 
764  // The vfs-mount syntax is:
765 
766  // vfs-mount system-filename mount-point [options]
767 
768  // The last two spaces mark the beginning of the mount point, and of the
769  // options, respectively. There might be multiple spaces in the system
770  // filename, which are part of the filename.
771 
772  // The last space marks the beginning of the mount point. Spaces before
773  // that are part of the system filename.
774  size_t space = mount_desc.rfind(' ');
775  if (space == string::npos) {
776  express_cat.warning()
777  << "No space in vfs-mount descriptor: " << mount_desc << "\n";
778 
779  } else {
780  string mount_point = mount_desc.substr(space + 1);
781  while (space > 0 && isspace(mount_desc[space - 1])) {
782  space--;
783  }
784  mount_desc = mount_desc.substr(0, space);
785  string options;
786 
787  space = mount_desc.rfind(' ');
788  if (space != string::npos) {
789  // If there's another space, we have the optional options field.
790  options = mount_point;
791  mount_point = mount_desc.substr(space + 1);
792  while (space > 0 && isspace(mount_desc[space - 1])) {
793  --space;
794  }
795  mount_desc = mount_desc.substr(0, space);
796  }
797 
798  mount_desc = ExecutionEnvironment::expand_string(mount_desc);
799  Filename physical_filename = Filename::from_os_specific(mount_desc);
800 
801  int flags;
802  string password;
803  parse_options(options, flags, password);
804  _global_ptr->mount(physical_filename, mount_point, flags, password);
805  }
806  }
807 
808  ConfigVariableString vfs_mount_ramdisk
809  ("vfs-mount-ramdisk", "",
810  PRC_DESC("vfs-mount-ramdisk mount-point [options]"));
811  if (!vfs_mount_ramdisk.empty()) {
812  string mount_point = vfs_mount_ramdisk;
813  string options;
814 
815  size_t space = mount_point.rfind(' ');
816  if (space != string::npos) {
817  // If there's a space, we have the optional options field.
818  options = mount_point.substr(space + 1);
819  while (space > 0 && isspace(mount_point[space - 1])) {
820  --space;
821  }
822  mount_point = mount_point.substr(0, space);
823  }
824 
825  int flags;
826  string password;
827  parse_options(options, flags, password);
828 
829  PT(VirtualFileMount) ramdisk = new VirtualFileMountRamdisk;
830  _global_ptr->mount(ramdisk, mount_point, flags);
831  }
832  }
833 
834  return _global_ptr;
835 }
836 
837 /**
838  * Convenience function; returns a newly allocated istream if the file exists
839  * and can be read, or NULL otherwise. Does not return an invalid istream.
840  *
841  * If auto_unwrap is true, an explicitly-named .pz file is automatically
842  * decompressed and the decompressed contents are returned. This is different
843  * than vfs-implicit-pz, which will automatically decompress a file if the
844  * extension .pz is *not* given.
845  */
846 istream *VirtualFileSystem::
847 open_read_file(const Filename &filename, bool auto_unwrap) const {
848  PT(VirtualFile) file = get_file(filename, false);
849  if (file == nullptr) {
850  return nullptr;
851  }
852  istream *str = file->open_read_file(auto_unwrap);
853  if (str != nullptr && str->fail()) {
854  close_read_file(str);
855  str = nullptr;
856  }
857  return str;
858 }
859 
860 /**
861  * Closes a file opened by a previous call to open_read_file(). This really
862  * just deletes the istream pointer, but it is recommended to use this
863  * interface instead of deleting it explicitly, to help work around compiler
864  * issues.
865  */
867 close_read_file(istream *stream) {
868  if (stream != nullptr) {
869  // For some reason--compiler bug in gcc 3.2?--explicitly deleting the
870  // stream pointer does not call the appropriate global delete function;
871  // instead apparently calling the system delete function. So we call the
872  // delete function by hand instead.
873 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
874  stream->~istream();
875  (*global_operator_delete)(stream);
876 #else
877  delete stream;
878 #endif
879  }
880 }
881 
882 /**
883  * Convenience function; returns a newly allocated ostream if the file exists
884  * and can be written, or NULL otherwise. Does not return an invalid ostream.
885  *
886  * If auto_wrap is true, an explicitly-named .pz file is automatically
887  * compressed while writing. If truncate is true, the file is truncated to
888  * zero length before writing.
889  */
890 ostream *VirtualFileSystem::
891 open_write_file(const Filename &filename, bool auto_wrap, bool truncate) {
892  PT(VirtualFile) file = create_file(filename);
893  if (file == nullptr) {
894  return nullptr;
895  }
896  ostream *str = file->open_write_file(auto_wrap, truncate);
897  if (str != nullptr && str->fail()) {
898  close_write_file(str);
899  str = nullptr;
900  }
901  return str;
902 }
903 
904 /**
905  * Works like open_write_file(), but the file is opened in append mode. Like
906  * open_write_file, the returned pointer should eventually be passed to
907  * close_write_file().
908  */
909 ostream *VirtualFileSystem::
910 open_append_file(const Filename &filename) {
911  PT(VirtualFile) file = create_file(filename);
912  if (file == nullptr) {
913  return nullptr;
914  }
915  ostream *str = file->open_append_file();
916  if (str != nullptr && str->fail()) {
917  close_write_file(str);
918  str = nullptr;
919  }
920  return str;
921 }
922 
923 /**
924  * Closes a file opened by a previous call to open_write_file(). This really
925  * just deletes the ostream pointer, but it is recommended to use this
926  * interface instead of deleting it explicitly, to help work around compiler
927  * issues.
928  */
930 close_write_file(ostream *stream) {
931  if (stream != nullptr) {
932 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
933  stream->~ostream();
934  (*global_operator_delete)(stream);
935 #else
936  delete stream;
937 #endif
938  }
939 }
940 
941 /**
942  * Convenience function; returns a newly allocated iostream if the file exists
943  * and can be written, or NULL otherwise. Does not return an invalid
944  * iostream.
945  */
946 iostream *VirtualFileSystem::
947 open_read_write_file(const Filename &filename, bool truncate) {
948  PT(VirtualFile) file = create_file(filename);
949  if (file == nullptr) {
950  return nullptr;
951  }
952  iostream *str = file->open_read_write_file(truncate);
953  if (str != nullptr && str->fail()) {
955  str = nullptr;
956  }
957  return str;
958 }
959 
960 /**
961  * Works like open_read_write_file(), but the file is opened in append mode.
962  * Like open_read_write_file, the returned pointer should eventually be passed
963  * to close_read_write_file().
964  */
965 iostream *VirtualFileSystem::
966 open_read_append_file(const Filename &filename) {
967  PT(VirtualFile) file = create_file(filename);
968  if (file == nullptr) {
969  return nullptr;
970  }
971  iostream *str = file->open_read_append_file();
972  if (str != nullptr && str->fail()) {
974  str = nullptr;
975  }
976  return str;
977 }
978 
979 /**
980  * Closes a file opened by a previous call to open_read_write_file(). This
981  * really just deletes the iostream pointer, but it is recommended to use this
982  * interface instead of deleting it explicitly, to help work around compiler
983  * issues.
984  */
986 close_read_write_file(iostream *stream) {
987  if (stream != nullptr) {
988 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW)
989  stream->~iostream();
990  (*global_operator_delete)(stream);
991 #else
992  delete stream;
993 #endif
994  }
995 }
996 
997 /**
998  * See Filename::atomic_compare_and_exchange_contents().
999  */
1001 atomic_compare_and_exchange_contents(const Filename &filename, string &orig_contents,
1002  const string &old_contents,
1003  const string &new_contents) {
1004  PT(VirtualFile) file = create_file(filename);
1005  if (file == nullptr) {
1006  return false;
1007  }
1008 
1009  return file->atomic_compare_and_exchange_contents(orig_contents, old_contents, new_contents);
1010 }
1011 
1012 /**
1013  * See Filename::atomic_read_contents().
1014  */
1016 atomic_read_contents(const Filename &filename, string &contents) const {
1017  PT(VirtualFile) file = get_file(filename, false);
1018  if (file == nullptr) {
1019  return false;
1020  }
1021 
1022  return file->atomic_read_contents(contents);
1023 }
1024 
1025 /**
1026  * Adds to names a list of all the mount points in use that are one directory
1027  * below path, if any. That is, these are the external files or directories
1028  * mounted directly to the indicated path.
1029  *
1030  * The names vector is filled with a set of basenames, the basename part of
1031  * the mount point.
1032  */
1034 scan_mount_points(vector_string &names, const Filename &path) const {
1035  nassertv(!path.empty() && !path.is_local());
1036  string prefix = path.get_fullpath().substr(1);
1037  Mounts::const_iterator mi;
1038  for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) {
1039  VirtualFileMount *mount = (*mi);
1040 
1041  string mount_point = mount->get_mount_point();
1042  if (prefix.empty()) {
1043  // The indicated path is the root. Is the mount point on the root?
1044  if (mount_point.find('/') == string::npos) {
1045  // No embedded slashes, so the mount point is only one directory below
1046  // the root.
1047  names.push_back(mount_point);
1048  }
1049  } else {
1050  if (mount_point.substr(0, prefix.length()) == prefix &&
1051  mount_point.length() > prefix.length() &&
1052  mount_point[prefix.length()] == '/') {
1053  // This mount point is below the indicated path. Is it only one
1054  // directory below?
1055  string basename = mount_point.substr(prefix.length());
1056  if (basename.find('/') == string::npos) {
1057  // No embedded slashes, so it's only one directory below.
1058  names.push_back(basename);
1059  }
1060  }
1061  }
1062  }
1063 }
1064 
1065 
1066 /**
1067  * Parses all of the option flags in the options list on the vfs-mount
1068  * Config.prc line.
1069  */
1071 parse_options(const string &options, int &flags, string &password) {
1072  flags = 0;
1073  password = string();
1074 
1075  // Split the options up by commas.
1076  size_t p = 0;
1077  size_t q = options.find(',', p);
1078  while (q != string::npos) {
1079  parse_option(options.substr(p, q - p),
1080  flags, password);
1081  p = q + 1;
1082  q = options.find(',', p);
1083  }
1084  parse_option(options.substr(p), flags, password);
1085 }
1086 
1087 /**
1088  * Parses one of the option flags in the options list on the vfs-mount
1089  * Config.prc line.
1090  */
1092 parse_option(const string &option, int &flags, string &password) {
1093  if (option == "0" || option.empty()) {
1094  // 0 is the null option.
1095  } else if (option == "ro") {
1096  flags |= MF_read_only;
1097  } else if (option.substr(0, 3) == "pw:") {
1098  password = option.substr(3);
1099  } else {
1100  express_cat.warning()
1101  << "Invalid option on vfs-mount: \"" << option << "\"\n";
1102  }
1103 }
1104 
1105 /**
1106  * Converts the mount point string supplied by the user to standard form
1107  * (relative to the current directory, with no double slashes, and not
1108  * terminating with a slash). The initial slash is removed.
1109  *
1110  * Assumes the lock is already held.
1111  */
1112 Filename VirtualFileSystem::
1113 normalize_mount_point(const Filename &mount_point) const {
1114  Filename nmp = mount_point;
1115  if (nmp.is_local()) {
1116  nmp = Filename(_cwd, mount_point);
1117  }
1118  nmp.standardize();
1119  nassertr(!nmp.empty() && nmp[0] == '/', nmp);
1120  return nmp.get_fullpath().substr(1);
1121 }
1122 
1123 /**
1124  * The private implementation of mount(). Assumes the lock is already held.
1125  */
1126 bool VirtualFileSystem::
1127 do_mount(VirtualFileMount *mount, const Filename &mount_point, int flags) {
1128  nassertr(mount->_file_system == nullptr, false);
1129  mount->_file_system = this;
1130  mount->_mount_point = normalize_mount_point(mount_point);
1131  mount->_mount_flags = flags;
1132  _mounts.push_back(mount);
1133  ++_mount_seq;
1134  return true;
1135 }
1136 
1137 /**
1138  * The private implementation of get_file(), create_file(), and
1139  * make_directory(). Assumes the lock is already held.
1140  */
1141 PT(VirtualFile) VirtualFileSystem::
1142 do_get_file(const Filename &filename, int open_flags) const {
1143  if (filename.empty()) {
1144  return nullptr;
1145  }
1146  Filename pathname(filename);
1147  if (pathname.is_local()) {
1148  pathname = Filename(_cwd, filename);
1149  if (filename.is_text()) {
1150  pathname.set_text();
1151  }
1152  }
1153  pathname.standardize();
1154  Filename strpath = pathname.get_filename_index(0).get_fullpath().substr(1);
1155  strpath.set_type(filename.get_type());
1156  // Also transparently look for a regular file suffixed .pz.
1157  Filename strpath_pz = strpath + ".pz";
1158 
1159  // Now scan all the mount points, from the back (since later mounts override
1160  // more recent ones), until a match is found.
1161  PT(VirtualFile) found_file = nullptr;
1162  VirtualFileComposite *composite_file = nullptr;
1163 
1164  // We use an index instead of an iterator, since the vector might change if
1165  // implicit mounts are added during this loop.
1166  unsigned int start_seq = _mount_seq;
1167 
1168  size_t i = _mounts.size();
1169  while (i > 0) {
1170  --i;
1171  VirtualFileMount *mount = _mounts[i];
1172  Filename mount_point = mount->get_mount_point();
1173  if (strpath == mount_point) {
1174  // Here's an exact match on the mount point. This filename is the root
1175  // directory of this mount object.
1176  if (consider_match(found_file, composite_file, mount, "", pathname,
1177  false, open_flags)) {
1178  return found_file;
1179  }
1180  } else if (mount_point.empty()) {
1181  // This is the root mount point; all files are in here.
1182  if (consider_match(found_file, composite_file, mount, strpath,
1183  pathname, false, open_flags)) {
1184  return found_file;
1185  }
1186 #ifdef HAVE_ZLIB
1187  if (vfs_implicit_pz) {
1188  if (consider_match(found_file, composite_file, mount, strpath_pz,
1189  pathname, true, open_flags)) {
1190  return found_file;
1191  }
1192  }
1193 #endif // HAVE_ZLIB
1194 
1195  } else if (strpath.length() > mount_point.length() &&
1196  mount_point == strpath.substr(0, mount_point.length()) &&
1197  strpath[mount_point.length()] == '/') {
1198  // This pathname falls within this mount system.
1199  Filename local_filename = strpath.substr(mount_point.length() + 1);
1200  Filename local_filename_pz = strpath_pz.substr(mount_point.length() + 1);
1201  if (consider_match(found_file, composite_file, mount, local_filename,
1202  pathname, false, open_flags)) {
1203  return found_file;
1204  }
1205 #ifdef HAVE_ZLIB
1206  if (vfs_implicit_pz) {
1207  // Bingo!
1208  if (consider_match(found_file, composite_file, mount, local_filename_pz,
1209  pathname, true, open_flags)) {
1210  return found_file;
1211  }
1212  }
1213 #endif // HAVE_ZLIB
1214  }
1215 
1216  // If we discover that a file has been implicitly mounted during one of
1217  // the above operations, start over from the beginning of the loop.
1218  if (start_seq != _mount_seq) {
1219  start_seq = _mount_seq;
1220  i = _mounts.size();
1221  }
1222  }
1223 
1224  if (found_file == nullptr && vfs_implicit_mf) {
1225  // The file wasn't found, as-is. Does it appear to be an implicit .mf
1226  // file reference?
1227  ((VirtualFileSystem *)this)->consider_mount_mf(filename);
1228 
1229  if (start_seq != _mount_seq) {
1230  // Yes, it was, or some nested file was. Now that we've implicitly
1231  // mounted the .mf file, go back and look again.
1232  return do_get_file(filename, open_flags);
1233  }
1234  }
1235 
1236 #if defined(_WIN32) && !defined(NDEBUG)
1237  if (!found_file) {
1238  // The file could not be found. Perhaps this is because the user passed
1239  // in a Windows-style path where a Unix-style path was expected?
1240  if (filename.length() > 2 && isalpha(filename[0]) && filename[1] == ':' &&
1241  (filename[2] == '\\' || filename[2] == '/')) {
1242 
1243  Filename corrected_fn = Filename::from_os_specific(filename);
1244  if (corrected_fn.exists()) {
1245  express_cat.warning()
1246  << "Filename uses Windows-style path: " << filename << "\n";
1247  express_cat.warning()
1248  << " expected Unix-style path: " << corrected_fn << "\n";
1249  }
1250  }
1251  }
1252 #endif
1253 
1254  return found_file;
1255 }
1256 
1257 /**
1258  * Evaluates one possible filename match found during a get_file() operation.
1259  * There may be multiple matches for a particular filename due to the
1260  * ambiguities introduced by allowing multiple mount points, so we may have to
1261  * keep searching even after the first match is found.
1262  *
1263  * Returns true if the search should terminate now, or false if it should keep
1264  * iterating.
1265  */
1266 bool VirtualFileSystem::
1267 consider_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_file,
1268  VirtualFileMount *mount, const Filename &local_filename,
1269  const Filename &original_filename, bool implicit_pz_file,
1270  int open_flags) const {
1271  PT(VirtualFile) vfile =
1272  mount->make_virtual_file(local_filename, original_filename, false, open_flags);
1273  if (!vfile->has_file() && ((open_flags & OF_allow_nonexist) == 0)) {
1274  // Keep looking.
1275  return false;
1276  }
1277 
1278  if (found_file == nullptr) {
1279  // This was our first match. Save it.
1280  found_file = vfile;
1281  if (!found_file->is_directory() || ((open_flags & OF_make_directory) != 0)) {
1282  // If it's not a directory (or we wanted to make a directory), we're
1283  // done.
1284  return true;
1285  }
1286  // It is a directory, so save it for later.
1287  if (implicit_pz_file) {
1288  // Don't look for directories named file.pz.
1289  found_file = nullptr;
1290  }
1291 
1292  } else {
1293  // This was our second match. The previous match(es) must have been
1294  // directories.
1295  if (!vfile->is_directory()) {
1296  // However, this one isn't a directory. We're done.
1297  return true;
1298  }
1299 
1300  if (!implicit_pz_file) {
1301  // At least two directories matched to the same path. We need a
1302  // composite directory.
1303  if (composite_file == nullptr) {
1304  composite_file =
1305  new VirtualFileComposite((VirtualFileSystem *)this, found_file->get_original_filename());
1306  composite_file->set_original_filename(original_filename);
1307  composite_file->add_component(found_file);
1308  found_file = composite_file;
1309  }
1310 
1311  composite_file->add_component(vfile);
1312  }
1313  }
1314 
1315  // Keep going, looking for more directories.
1316  return false;
1317 }
1318 
1319 /**
1320  * The indicated filename was not found. Check to see if it is using an
1321  * implicit reference to a .mf file as a directory, that hasn't already been
1322  * mounted. If it is, mount the .mf file in-place, and return true; if it is
1323  * not, or if its .mf file is already mounted in-place, return false.
1324  *
1325  * Assumes the lock is already held.
1326  */
1327 bool VirtualFileSystem::
1328 consider_mount_mf(const Filename &filename) {
1329  Filename dirname = filename.get_dirname();
1330  if (dirname.empty() || dirname == filename) {
1331  // Reached the top directory; no .mf file references.
1332  return false;
1333  }
1334  if (is_directory(dirname)) {
1335  // Reached a real (or already-mounted) directory; no unmounted .mf file
1336  // references.
1337  return false;
1338  }
1339  if (dirname.get_extension() == "mf") {
1340  // Hey, here's a multifile reference!
1341  dirname.set_binary();
1342  PT(VirtualFile) file = do_get_file(dirname, false);
1343  if (file == nullptr || !file->is_regular_file()) {
1344  // Oh, never mind. Not a real file.
1345  return false;
1346  }
1347 
1348  PT(Multifile) multifile = new Multifile;
1349 
1350  istream *stream = file->open_read_file(false);
1351  if (stream == nullptr) {
1352  // Couldn't read file.
1353  return false;
1354  }
1355 
1356  // Wrap a thread-safe wrapper around that stream, so multiple threads can
1357  // safely read the multifile simultaneously.
1358  IStreamWrapper *streamw = new IStreamWrapper(stream, true);
1359 
1360  if (!multifile->open_read(streamw, true)) {
1361  // Invalid multifile.
1362  return false;
1363  }
1364 
1365  multifile->set_multifile_name(dirname.get_basename());
1366  express_cat->info()
1367  << "Implicitly mounting " << dirname << "\n";
1368 
1369  PT(VirtualFileMountMultifile) new_mount =
1370  new VirtualFileMountMultifile(multifile);
1371  return do_mount(new_mount, dirname, MF_read_only);
1372  }
1373 
1374  // Recurse.
1375  return consider_mount_mf(dirname);
1376 }
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 copy_file(const Filename &orig_filename, const Filename &new_filename)
Attempts to copy the contents of the indicated file to the indicated file.
std::iostream * open_read_append_file(const Filename &filename)
Works like open_read_write_file(), but the file is opened in append mode.
PointerTo< VirtualFile > find_file(const Filename &filename, const DSearchPath &searchpath, bool status_only=false) const
Uses the indicated search path to find the file within the file system.
std::string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:358
void set_extension(const std::string &s)
Replaces the file extension.
Definition: filename.cxx:804
bool rename_file(const Filename &orig_filename, const Filename &new_filename)
Attempts to move or rename the indicated file or directory.
Multifile * get_multifile() const
Returns the Multifile pointer that this mount object is based on.
int find_all_files(const Filename &filename, const DSearchPath &searchpath, DSearchPath::Results &results) const
Searches all the directories in the search list for the indicated file, in order. ...
static void close_read_write_file(std::iostream *stream)
Closes a file opened by a previous call to open_read_write_file().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
size_t get_num_unique_values() const
Returns the number of unique values in the variable.
Maps an actual OS directory into the VirtualFileSystem.
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS&#39;s 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...
void add_component(VirtualFile *file)
Adds one more component to the composite directory.
void set_type(Type type)
Sets the type of the file represented by the filename.
Definition: filename.I:468
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:414
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.
void init_libexpress()
Initializes the library.
bool atomic_read_contents(const Filename &filename, std::string &contents) const
See Filename::atomic_read_contents().
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
bool make_directory_full(const Filename &filename)
Attempts to create a directory within the file system.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
bool is_fully_qualified() const
Returns true if the filename is fully qualified, e.g.
Definition: filename.I:562
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class is similar to ConfigVariable, but it reports its value as a list of strings.
PointerTo< VirtualFile > create_file(const Filename &filename)
Attempts to create a file by the indicated name in the filesystem, if possible, and returns it...
void standardize()
Converts the filename to standard form by replacing consecutive slashes with a single slash...
Definition: filename.cxx:900
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Simulates an actual directory on disk with in-memory storage.
Type get_type() const
Returns the type of the file represented by the filename, as previously set by set_type().
Definition: filename.I:485
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_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.
get_num_directories
Returns the number of directories on the search list.
Definition: dSearchPath.h:76
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists.
bool mount(Multifile *multifile, const Filename &mount_point, int flags)
Mounts the indicated Multifile at the given mount point.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool is_directory(const Filename &filename) const
Convenience function; returns true if the named file exists and is a directory.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
This is a convenience class to specialize ConfigVariable as a string type.
static std::string expand_string(const std::string &str)
Reads the string, looking for environment variable names marked by a $.
This class provides a locking wrapper around an arbitrary istream pointer.
Definition: streamWrapper.h:59
bool make_directory(const Filename &filename)
Attempts to create a directory within the file system.
virtual bool is_directory() const
Returns true if this file represents a directory (and scan_directory() may be called), false otherwise.
Definition: virtualFile.cxx:41
std::string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition: filename.I:338
bool chdir(const Filename &new_directory)
Changes the current directory.
std::ostream * open_append_file(const Filename &filename)
Works like open_write_file(), but the file is opened in append mode.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
void scan_mount_points(vector_string &names, const Filename &path) const
Adds to names a list of all the mount points in use that are one directory below path, if any.
const Filename & get_physical_filename() const
Returns the name of the source file on the OS filesystem of the directory or file that is mounted...
int unmount_point(const Filename &mount_point)
Unmounts all systems attached to the given mount point from the file system.
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
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
const Filename & get_original_filename() const
Returns the original filename as it was used to locate this VirtualFile.
Definition: virtualFile.I:27
int unmount(Multifile *multifile)
Unmounts all appearances of the indicated Multifile from the file system.
std::string get_unique_value(size_t n) const
Returns the nth unique value of the variable.
bool is_local() const
Returns true if the filename is local, e.g.
Definition: filename.I:549
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
std::string get_extension() const
Returns the file extension.
Definition: filename.I:400
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
static void close_write_file(std::ostream *stream)
Closes a file opened by a previous call to open_write_file().
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.
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...
bool is_directory() const
Returns true if the filename exists and is a directory name, false otherwise.
Definition: filename.cxx:1359
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...
The abstract base class for a mount definition used within a VirtualFileSystem.
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...
get_directory
Returns the nth directory on the search list.
Definition: dSearchPath.h:76
A file that contains a set of files.
Definition: multifile.h:37
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void write(std::ostream &out) const
Print debugging information.
This class stores a list of directories that can be searched, in order, to locate a particular file...
Definition: dSearchPath.h:28
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Filename get_cwd() const
Returns the current directory name.
get_mount
Returns the nth mount in the system.
int unmount_all()
Unmounts all files from the file system.
const Filename & get_multifile_name() const
Returns the filename of the Multifile, if it is available.
Definition: multifile.I:18
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool delete_file(const Filename &filename)
Attempts to delete the indicated file or directory.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A composite directory within the VirtualFileSystem: this maps to more than one directory on different...
Maps a Multifile&#39;s contents into the VirtualFileSystem.
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().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1267
void add_file(const Filename &file)
Adds a new file to the result list.
Definition: dSearchPath.cxx:83
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, and no drive letter) based on the supplied filename string that describes a filename in the local system conventions (for instance, on Windows, it may use backslashes or begin with a drive letter and a colon).
Definition: filename.cxx:328