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,...
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),...
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,...
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'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,...
Definition: filename.cxx:328