Panda3D
|
00001 // Filename: virtualFileSystem.cxx 00002 // Created by: drose (03Aug02) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "virtualFileSystem.h" 00016 #include "virtualFileSimple.h" 00017 #include "virtualFileComposite.h" 00018 #include "virtualFileMount.h" 00019 #include "virtualFileMountMultifile.h" 00020 #include "virtualFileMountSystem.h" 00021 #include "streamWrapper.h" 00022 #include "dSearchPath.h" 00023 #include "dcast.h" 00024 #include "config_express.h" 00025 #include "executionEnvironment.h" 00026 #include "pset.h" 00027 00028 VirtualFileSystem *VirtualFileSystem::_global_ptr = NULL; 00029 00030 00031 //////////////////////////////////////////////////////////////////// 00032 // Function: VirtualFileSystem::Constructor 00033 // Access: Published 00034 // Description: 00035 //////////////////////////////////////////////////////////////////// 00036 VirtualFileSystem:: 00037 VirtualFileSystem() : 00038 vfs_case_sensitive 00039 ("vfs-case-sensitive", 00040 #ifdef NDEBUG 00041 false, // The default for a production build is not case-sensitive; 00042 // this avoids runtime overhead to verify case sensitivity. 00043 #else 00044 true, 00045 #endif 00046 PRC_DESC("Set this true to make the VirtualFileSystem present the native " 00047 "OS-provided filesystem as if it were a case-sensitive file " 00048 "system, even if it is not (e.g. on Windows). This variable " 00049 "has no effect if the native filesystem is already case-sensitive, " 00050 "and it has no effect on mounted multifile systems, which are " 00051 "always case-sensitive.")), 00052 vfs_implicit_pz 00053 ("vfs-implicit-pz", true, 00054 PRC_DESC("When this is true, the VirtualFileSystem will pretend a named " 00055 "file exists even if it doesn't, as long as a filename with the " 00056 "same name and the additional extension .pz does exist. In this " 00057 "case, the VirtualFileSystem will implicitly open the .pz file " 00058 "and decompress it on-the-fly.")), 00059 vfs_implicit_mf 00060 ("vfs-implicit-mf", false, 00061 PRC_DESC("When this is true, the VirtualFileSystem will automatically " 00062 "mount multifiles on-the-fly when they are used as directories. " 00063 "For instance, opening the file /c/files/foo.mf/dirname/mytex.jpg " 00064 "will implicitly retrieve a file named 'dirname/mytex.jpg' " 00065 "within the multifile /c/files/foo.mf, even if the multifile " 00066 "has not already been mounted. This makes all of your multifiles " 00067 "act like directories.")) 00068 { 00069 _cwd = "/"; 00070 _mount_seq = 0; 00071 } 00072 00073 //////////////////////////////////////////////////////////////////// 00074 // Function: VirtualFileSystem::Destructor 00075 // Access: Published 00076 // Description: 00077 //////////////////////////////////////////////////////////////////// 00078 VirtualFileSystem:: 00079 ~VirtualFileSystem() { 00080 unmount_all(); 00081 } 00082 00083 //////////////////////////////////////////////////////////////////// 00084 // Function: VirtualFileSystem::mount 00085 // Access: Published 00086 // Description: Mounts the indicated Multifile at the given mount 00087 // point. 00088 //////////////////////////////////////////////////////////////////// 00089 bool VirtualFileSystem:: 00090 mount(Multifile *multifile, const Filename &mount_point, int flags) { 00091 PT(VirtualFileMountMultifile) new_mount = 00092 new VirtualFileMountMultifile(multifile); 00093 return mount(new_mount, mount_point, flags); 00094 } 00095 00096 //////////////////////////////////////////////////////////////////// 00097 // Function: VirtualFileSystem::mount 00098 // Access: Published 00099 // Description: Mounts the indicated system file or directory at the 00100 // given mount point. If the named file is a directory, 00101 // mounts the directory. If the named file is a 00102 // Multifile, mounts it as a Multifile. Returns true on 00103 // success, false on failure. 00104 // 00105 // A given system directory may be mounted to multiple 00106 // different mount point, and the same mount point may 00107 // share multiple system directories. In the case of 00108 // ambiguities (that is, two different files with 00109 // exactly the same full pathname), the most-recently 00110 // mounted system wins. 00111 // 00112 // The filename specified as the first parameter must 00113 // refer to a real, physical filename on disk; it cannot 00114 // be a virtual file already appearing within the vfs 00115 // filespace. However, it is possible to mount such a 00116 // file; see mount_loop() for this. 00117 //// 00118 // Note that a mounted VirtualFileSystem directory is 00119 // fully case-sensitive, unlike the native Windows file 00120 // system, so you must refer to files within the virtual 00121 // file system with exactly the right case. 00122 //////////////////////////////////////////////////////////////////// 00123 bool VirtualFileSystem:: 00124 mount(const Filename &physical_filename, const Filename &mount_point, 00125 int flags, const string &password) { 00126 if (!physical_filename.exists()) { 00127 express_cat->warning() 00128 << "Attempt to mount " << physical_filename << ", not found.\n"; 00129 return false; 00130 } 00131 00132 if (physical_filename.is_directory()) { 00133 PT(VirtualFileMountSystem) new_mount = 00134 new VirtualFileMountSystem(physical_filename); 00135 return mount(new_mount, mount_point, flags); 00136 } else { 00137 // It's not a directory; it must be a Multifile. 00138 PT(Multifile) multifile = new Multifile; 00139 multifile->set_encryption_password(password); 00140 00141 // For now these are always opened read only. Maybe later we'll 00142 // support read-write on Multifiles. 00143 flags |= MF_read_only; 00144 if (!multifile->open_read(physical_filename)) { 00145 return false; 00146 } 00147 00148 return mount(multifile, mount_point, flags); 00149 } 00150 } 00151 00152 //////////////////////////////////////////////////////////////////// 00153 // Function: VirtualFileSystem::mount_loop 00154 // Access: Published 00155 // Description: This is similar to mount(), but it receives the name 00156 // of a Multifile that already appears within the 00157 // virtual file system. It can be used to mount a 00158 // Multifile that is itself hosted within a 00159 // virtually-mounted Multifile. 00160 // 00161 // This interface can also be used to mount physical 00162 // files (that appear within the virtual filespace), but 00163 // it cannot be used to mount directories. Use mount() 00164 // if you need to mount a directory. 00165 // 00166 // Note that there is additional overhead, in the form 00167 // of additional buffer copies of the data, for 00168 // recursively mounting a multifile like this. 00169 //////////////////////////////////////////////////////////////////// 00170 bool VirtualFileSystem:: 00171 mount_loop(const Filename &virtual_filename, const Filename &mount_point, 00172 int flags, const string &password) { 00173 PT(VirtualFile) file = get_file(virtual_filename, false); 00174 if (file == NULL) { 00175 express_cat->warning() 00176 << "Attempt to mount " << virtual_filename << ", not found.\n"; 00177 return false; 00178 } 00179 00180 if (file->is_directory()) { 00181 PT(VirtualFileMountSystem) new_mount = 00182 new VirtualFileMountSystem(virtual_filename); 00183 return mount(new_mount, mount_point, flags); 00184 00185 } else { 00186 // It's not a directory; it must be a Multifile. 00187 PT(Multifile) multifile = new Multifile; 00188 multifile->set_encryption_password(password); 00189 00190 // For now these are always opened read only. Maybe later we'll 00191 // support read-write on Multifiles. 00192 flags |= MF_read_only; 00193 if (!multifile->open_read(virtual_filename)) { 00194 return false; 00195 } 00196 00197 return mount(multifile, mount_point, flags); 00198 } 00199 } 00200 00201 //////////////////////////////////////////////////////////////////// 00202 // Function: VirtualFileSystem::mount 00203 // Access: Published 00204 // Description: Adds the given VirtualFileMount object to the mount 00205 // list. This is a lower-level function that the other 00206 // flavors of mount(); it requires you to create a 00207 // VirtualFileMount object specifically. 00208 //////////////////////////////////////////////////////////////////// 00209 bool VirtualFileSystem:: 00210 mount(VirtualFileMount *mount, const Filename &mount_point, int flags) { 00211 if (express_cat->is_debug()) { 00212 express_cat->debug() 00213 << "mount " << *mount << " under " << mount_point << "\n"; 00214 } 00215 00216 _lock.acquire(); 00217 bool result = do_mount(mount, mount_point, flags); 00218 _lock.release(); 00219 return result; 00220 } 00221 00222 //////////////////////////////////////////////////////////////////// 00223 // Function: VirtualFileSystem::unmount 00224 // Access: Published 00225 // Description: Unmounts all appearances of the indicated Multifile 00226 // from the file system. Returns the number of 00227 // appearances unmounted. 00228 //////////////////////////////////////////////////////////////////// 00229 int VirtualFileSystem:: 00230 unmount(Multifile *multifile) { 00231 _lock.acquire(); 00232 Mounts::iterator ri, wi; 00233 wi = ri = _mounts.begin(); 00234 while (ri != _mounts.end()) { 00235 VirtualFileMount *mount = (*ri); 00236 (*wi) = mount; 00237 00238 if (mount->is_exact_type(VirtualFileMountMultifile::get_class_type())) { 00239 VirtualFileMountMultifile *mmount = 00240 DCAST(VirtualFileMountMultifile, mount); 00241 if (mmount->get_multifile() == multifile) { 00242 // Remove this one. Don't increment wi. 00243 if (express_cat->is_debug()) { 00244 express_cat->debug() 00245 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n"; 00246 } 00247 mount->_file_system = NULL; 00248 00249 } else { 00250 // Don't remove this one. 00251 ++wi; 00252 } 00253 } else { 00254 // Don't remove this one. 00255 ++wi; 00256 } 00257 ++ri; 00258 } 00259 00260 int num_removed = _mounts.end() - wi; 00261 _mounts.erase(wi, _mounts.end()); 00262 ++_mount_seq; 00263 _lock.release(); 00264 return num_removed; 00265 } 00266 00267 //////////////////////////////////////////////////////////////////// 00268 // Function: VirtualFileSystem::unmount 00269 // Access: Published 00270 // Description: Unmounts all appearances of the indicated directory 00271 // name or multifile name from the file system. Returns 00272 // the number of appearances unmounted. 00273 //////////////////////////////////////////////////////////////////// 00274 int VirtualFileSystem:: 00275 unmount(const Filename &physical_filename) { 00276 _lock.acquire(); 00277 Mounts::iterator ri, wi; 00278 wi = ri = _mounts.begin(); 00279 while (ri != _mounts.end()) { 00280 VirtualFileMount *mount = (*ri); 00281 (*wi) = mount; 00282 00283 if (mount->is_exact_type(VirtualFileMountSystem::get_class_type())) { 00284 VirtualFileMountSystem *smount = 00285 DCAST(VirtualFileMountSystem, mount); 00286 if (smount->get_physical_filename() == physical_filename) { 00287 // Remove this one. Don't increment wi. 00288 if (express_cat->is_debug()) { 00289 express_cat->debug() 00290 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n"; 00291 } 00292 mount->_file_system = NULL; 00293 00294 } else { 00295 // Don't remove this one. 00296 ++wi; 00297 } 00298 00299 } else if (mount->is_exact_type(VirtualFileMountMultifile::get_class_type())) { 00300 VirtualFileMountMultifile *mmount = 00301 DCAST(VirtualFileMountMultifile, mount); 00302 if (mmount->get_multifile()->get_multifile_name() == physical_filename) { 00303 // Remove this one. Don't increment wi. 00304 if (express_cat->is_debug()) { 00305 express_cat->debug() 00306 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n"; 00307 } 00308 mount->_file_system = NULL; 00309 00310 } else { 00311 // Don't remove this one. 00312 ++wi; 00313 } 00314 00315 } else { 00316 // Don't remove this one. 00317 ++wi; 00318 } 00319 ++ri; 00320 } 00321 00322 int num_removed = _mounts.end() - wi; 00323 _mounts.erase(wi, _mounts.end()); 00324 ++_mount_seq; 00325 _lock.release(); 00326 return num_removed; 00327 } 00328 00329 //////////////////////////////////////////////////////////////////// 00330 // Function: VirtualFileSystem::unmount 00331 // Access: Published 00332 // Description: Unmounts the indicated VirtualFileMount object 00333 // from the file system. Returns the number of 00334 // appearances unmounted. 00335 //////////////////////////////////////////////////////////////////// 00336 int VirtualFileSystem:: 00337 unmount(VirtualFileMount *mount) { 00338 _lock.acquire(); 00339 Mounts::iterator ri, wi; 00340 wi = ri = _mounts.begin(); 00341 while (ri != _mounts.end()) { 00342 (*wi) = (*ri); 00343 if ((*ri) == mount) { 00344 // Remove this one. Don't increment wi. 00345 if (express_cat->is_debug()) { 00346 express_cat->debug() 00347 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n"; 00348 } 00349 (*ri)->_file_system = NULL; 00350 00351 } else { 00352 // Don't remove this one. 00353 ++wi; 00354 } 00355 ++ri; 00356 } 00357 00358 int num_removed = _mounts.end() - wi; 00359 _mounts.erase(wi, _mounts.end()); 00360 ++_mount_seq; 00361 _lock.release(); 00362 return num_removed; 00363 } 00364 00365 //////////////////////////////////////////////////////////////////// 00366 // Function: VirtualFileSystem::unmount_point 00367 // Access: Published 00368 // Description: Unmounts all systems attached to the given mount 00369 // point from the file system. Returns the number of 00370 // appearances unmounted. 00371 //////////////////////////////////////////////////////////////////// 00372 int VirtualFileSystem:: 00373 unmount_point(const Filename &mount_point) { 00374 _lock.acquire(); 00375 Filename nmp = normalize_mount_point(mount_point); 00376 Mounts::iterator ri, wi; 00377 wi = ri = _mounts.begin(); 00378 while (ri != _mounts.end()) { 00379 VirtualFileMount *mount = (*ri); 00380 (*wi) = mount; 00381 00382 if (mount->get_mount_point() == nmp) { 00383 // Remove this one. Don't increment wi. 00384 if (express_cat->is_debug()) { 00385 express_cat->debug() 00386 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n"; 00387 } 00388 mount->_file_system = NULL; 00389 00390 } else { 00391 // Don't remove this one. 00392 ++wi; 00393 } 00394 ++ri; 00395 } 00396 00397 int num_removed = _mounts.end() - wi; 00398 _mounts.erase(wi, _mounts.end()); 00399 ++_mount_seq; 00400 _lock.release(); 00401 return num_removed; 00402 } 00403 00404 //////////////////////////////////////////////////////////////////// 00405 // Function: VirtualFileSystem::unmount_all 00406 // Access: Published 00407 // Description: Unmounts all files from the file system. Returns the 00408 // number of systems unmounted. 00409 //////////////////////////////////////////////////////////////////// 00410 int VirtualFileSystem:: 00411 unmount_all() { 00412 _lock.acquire(); 00413 Mounts::iterator ri; 00414 for (ri = _mounts.begin(); ri != _mounts.end(); ++ri) { 00415 VirtualFileMount *mount = (*ri); 00416 if (express_cat->is_debug()) { 00417 express_cat->debug() 00418 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n"; 00419 } 00420 mount->_file_system = NULL; 00421 } 00422 00423 int num_removed = _mounts.size(); 00424 _mounts.clear(); 00425 ++_mount_seq; 00426 _lock.release(); 00427 return num_removed; 00428 } 00429 00430 //////////////////////////////////////////////////////////////////// 00431 // Function: VirtualFileSystem::get_num_mounts 00432 // Access: Published 00433 // Description: Returns the number of individual mounts in the 00434 // system. 00435 //////////////////////////////////////////////////////////////////// 00436 int VirtualFileSystem:: 00437 get_num_mounts() const { 00438 ((VirtualFileSystem *)this)->_lock.acquire(); 00439 int result = _mounts.size(); 00440 ((VirtualFileSystem *)this)->_lock.release(); 00441 return result; 00442 } 00443 00444 //////////////////////////////////////////////////////////////////// 00445 // Function: VirtualFileSystem::get_mount 00446 // Access: Published 00447 // Description: Returns the nth mount in the system. 00448 //////////////////////////////////////////////////////////////////// 00449 PT(VirtualFileMount) VirtualFileSystem:: 00450 get_mount(int n) const { 00451 ((VirtualFileSystem *)this)->_lock.acquire(); 00452 nassertd(n >= 0 && n < (int)_mounts.size()) { 00453 ((VirtualFileSystem *)this)->_lock.release(); 00454 return NULL; 00455 } 00456 PT(VirtualFileMount) result = _mounts[n]; 00457 ((VirtualFileSystem *)this)->_lock.release(); 00458 return result; 00459 } 00460 00461 //////////////////////////////////////////////////////////////////// 00462 // Function: VirtualFileSystem::chdir 00463 // Access: Published 00464 // Description: Changes the current directory. This is used to 00465 // resolve relative pathnames in get_file() and/or 00466 // find_file(). Returns true if successful, false 00467 // otherwise. 00468 //////////////////////////////////////////////////////////////////// 00469 bool VirtualFileSystem:: 00470 chdir(const Filename &new_directory) { 00471 _lock.acquire(); 00472 if (new_directory == "/") { 00473 // We can always return to the root. 00474 _cwd = new_directory; 00475 _lock.release(); 00476 return true; 00477 } 00478 00479 PT(VirtualFile) file = do_get_file(new_directory, true); 00480 if (file != (VirtualFile *)NULL && file->is_directory()) { 00481 _cwd = file->get_filename(); 00482 _lock.release(); 00483 return true; 00484 } 00485 _lock.release(); 00486 return false; 00487 } 00488 00489 //////////////////////////////////////////////////////////////////// 00490 // Function: VirtualFileSystem::get_cwd 00491 // Access: Published 00492 // Description: Returns the current directory name. See chdir(). 00493 //////////////////////////////////////////////////////////////////// 00494 Filename VirtualFileSystem:: 00495 get_cwd() const { 00496 ((VirtualFileSystem *)this)->_lock.acquire(); 00497 Filename result = _cwd; 00498 ((VirtualFileSystem *)this)->_lock.release(); 00499 return result; 00500 } 00501 00502 //////////////////////////////////////////////////////////////////// 00503 // Function: VirtualFileSystem::get_file 00504 // Access: Published 00505 // Description: Looks up the file by the indicated name in the file 00506 // system. Returns a VirtualFile pointer representing 00507 // the file if it is found, or NULL if it is not. 00508 // 00509 // If status_only is true, the file will be checked for 00510 // existence and length and so on, but the returned 00511 // file's contents cannot be read. This is an 00512 // optimization which is especially important for 00513 // certain mount types, for instance HTTP, for which 00514 // opening a file to determine its status is 00515 // substantially less expensive than opening it to read 00516 // its contents. 00517 //////////////////////////////////////////////////////////////////// 00518 PT(VirtualFile) VirtualFileSystem:: 00519 get_file(const Filename &filename, bool status_only) const { 00520 ((VirtualFileSystem *)this)->_lock.acquire(); 00521 PT(VirtualFile) result = do_get_file(filename, status_only); 00522 ((VirtualFileSystem *)this)->_lock.release(); 00523 return result; 00524 } 00525 00526 //////////////////////////////////////////////////////////////////// 00527 // Function: VirtualFileSystem::find_file 00528 // Access: Published 00529 // Description: Uses the indicated search path to find the file 00530 // within the file system. Returns the first occurrence 00531 // of the file found, or NULL if the file cannot be 00532 // found. 00533 //////////////////////////////////////////////////////////////////// 00534 PT(VirtualFile) VirtualFileSystem:: 00535 find_file(const Filename &filename, const DSearchPath &searchpath, 00536 bool status_only) const { 00537 if (!filename.is_local()) { 00538 return get_file(filename, status_only); 00539 } 00540 00541 int num_directories = searchpath.get_num_directories(); 00542 for (int i = 0; i < num_directories; ++i) { 00543 Filename match(searchpath.get_directory(i), filename); 00544 if (searchpath.get_directory(i) == "." && 00545 filename.is_fully_qualified()) { 00546 // A special case for the "." directory: to avoid prefixing an 00547 // endless stream of ./ in front of files, if the filename 00548 // already has a ./ prefixed (i.e. is_fully_qualified() is 00549 // true), we don't prefix another one. 00550 match = filename; 00551 } 00552 PT(VirtualFile) found_file = get_file(match, status_only); 00553 if (found_file != (VirtualFile *)NULL) { 00554 return found_file; 00555 } 00556 } 00557 00558 return NULL; 00559 } 00560 00561 00562 //////////////////////////////////////////////////////////////////// 00563 // Function: VirtualFileSystem::resolve_filename 00564 // Access: Public 00565 // Description: Searches the given search path for the filename. If 00566 // it is found, updates the filename to the full 00567 // pathname found and returns true; otherwise, returns 00568 // false. 00569 //////////////////////////////////////////////////////////////////// 00570 bool VirtualFileSystem:: 00571 resolve_filename(Filename &filename, 00572 const DSearchPath &searchpath, 00573 const string &default_extension) const { 00574 PT(VirtualFile) found; 00575 00576 if (filename.is_local()) { 00577 found = find_file(filename, searchpath, true); 00578 00579 if (found.is_null()) { 00580 // We didn't find it with the given extension; can we try the 00581 // default extension? 00582 if (filename.get_extension().empty() && !default_extension.empty()) { 00583 Filename try_ext = filename; 00584 try_ext.set_extension(default_extension); 00585 found = find_file(try_ext, searchpath, true); 00586 } 00587 } 00588 } else { 00589 if (exists(filename)) { 00590 // The full pathname exists. Return true. 00591 return true; 00592 } else { 00593 // The full pathname doesn't exist with the given extension; 00594 // does it exist with the default extension? 00595 if (filename.get_extension().empty() && !default_extension.empty()) { 00596 Filename try_ext = filename; 00597 try_ext.set_extension(default_extension); 00598 found = get_file(try_ext, true); 00599 } 00600 } 00601 } 00602 00603 if (!found.is_null()) { 00604 filename = found->get_original_filename(); 00605 return true; 00606 } 00607 00608 return false; 00609 } 00610 00611 //////////////////////////////////////////////////////////////////// 00612 // Function: VirtualFileSystem::find_all_files 00613 // Access: Public 00614 // Description: Searches all the directories in the search list for 00615 // the indicated file, in order. Fills up the results 00616 // list with *all* of the matching filenames found, if 00617 // any. Returns the number of matches found. 00618 // 00619 // It is the responsibility of the the caller to clear 00620 // the results list first; otherwise, the newly-found 00621 // files will be appended to the list. 00622 //////////////////////////////////////////////////////////////////// 00623 int VirtualFileSystem:: 00624 find_all_files(const Filename &filename, const DSearchPath &searchpath, 00625 DSearchPath::Results &results) const { 00626 int num_added = 0; 00627 00628 if (filename.is_local()) { 00629 int num_directories = searchpath.get_num_directories(); 00630 for (int i = 0; i < num_directories; ++i) { 00631 Filename match(searchpath.get_directory(i), filename); 00632 if (exists(match)) { 00633 if (searchpath.get_directory(i) == "." && 00634 filename.is_fully_qualified()) { 00635 // A special case for the "." directory: to avoid prefixing 00636 // an endless stream of ./ in front of files, if the 00637 // filename already has a ./ prefixed 00638 // (i.e. is_fully_fully_qualified() is true), we don't 00639 // prefix another one. 00640 results.add_file(filename); 00641 } else { 00642 results.add_file(match); 00643 } 00644 ++num_added; 00645 } 00646 } 00647 } 00648 00649 return num_added; 00650 } 00651 00652 //////////////////////////////////////////////////////////////////// 00653 // Function: VirtualFileSystem::write 00654 // Access: Published 00655 // Description: Print debugging information. 00656 // (e.g. from Python or gdb prompt). 00657 //////////////////////////////////////////////////////////////////// 00658 void VirtualFileSystem:: 00659 write(ostream &out) const { 00660 ((VirtualFileSystem *)this)->_lock.acquire(); 00661 Mounts::const_iterator mi; 00662 for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) { 00663 VirtualFileMount *mount = (*mi); 00664 mount->write(out); 00665 } 00666 ((VirtualFileSystem *)this)->_lock.release(); 00667 } 00668 00669 00670 //////////////////////////////////////////////////////////////////// 00671 // Function: VirtualFileSystem::get_global_ptr 00672 // Access: Published, Static 00673 // Description: Returns the default global VirtualFileSystem. You 00674 // may create your own personal VirtualFileSystem 00675 // objects and use them for whatever you like, but Panda 00676 // will attempt to load models and stuff from this 00677 // default object. 00678 // 00679 // Initially, the global VirtualFileSystem is set up to 00680 // mount the OS filesystem to root; i.e. it is 00681 // equivalent to the OS filesystem. This may be 00682 // subsequently adjusted by the user. 00683 //////////////////////////////////////////////////////////////////// 00684 VirtualFileSystem *VirtualFileSystem:: 00685 get_global_ptr() { 00686 if (_global_ptr == (VirtualFileSystem *)NULL) { 00687 // Make sure this is initialized. 00688 init_libexpress(); 00689 00690 _global_ptr = new VirtualFileSystem; 00691 00692 // Set up the default mounts. First, there is always the root 00693 // mount. 00694 _global_ptr->mount("/", "/", 0); 00695 00696 // And our initial cwd comes from the environment. 00697 _global_ptr->chdir(ExecutionEnvironment::get_cwd()); 00698 00699 // Then, we add whatever mounts are listed in the Configrc file. 00700 ConfigVariableList mounts 00701 ("vfs-mount", 00702 PRC_DESC("vfs-mount system-filename mount-point [options]")); 00703 00704 int num_unique_values = mounts.get_num_unique_values(); 00705 for (int i = 0; i < num_unique_values; i++) { 00706 string mount_desc = mounts.get_unique_value(i); 00707 00708 // The vfs-mount syntax is: 00709 00710 // vfs-mount system-filename mount-point [options] 00711 00712 // The last two spaces mark the beginning of the mount point, 00713 // and of the options, respectively. There might be multiple 00714 // spaces in the system filename, which are part of the 00715 // filename. 00716 00717 // The last space marks the beginning of the mount point. 00718 // Spaces before that are part of the system filename. 00719 size_t space = mount_desc.rfind(' '); 00720 if (space == string::npos) { 00721 express_cat.warning() 00722 << "No space in vfs-mount descriptor: " << mount_desc << "\n"; 00723 00724 } else { 00725 string mount_point = mount_desc.substr(space + 1); 00726 while (space > 0 && isspace(mount_desc[space - 1])) { 00727 space--; 00728 } 00729 mount_desc = mount_desc.substr(0, space); 00730 string options; 00731 00732 space = mount_desc.rfind(' '); 00733 if (space != string::npos) { 00734 // If there's another space, we have the optional options field. 00735 options = mount_point; 00736 mount_point = mount_desc.substr(space + 1); 00737 while (space > 0 && isspace(mount_desc[space - 1])) { 00738 --space; 00739 } 00740 mount_desc = mount_desc.substr(0, space); 00741 } 00742 00743 mount_desc = ExecutionEnvironment::expand_string(mount_desc); 00744 Filename physical_filename = Filename::from_os_specific(mount_desc); 00745 00746 int flags = 0; 00747 string password; 00748 00749 // Split the options up by commas. 00750 size_t p = 0; 00751 size_t q = options.find(',', p); 00752 while (q != string::npos) { 00753 parse_option(options.substr(p, q - p), 00754 flags, password); 00755 p = q + 1; 00756 q = options.find(',', p); 00757 } 00758 parse_option(options.substr(p), flags, password); 00759 00760 _global_ptr->mount(physical_filename, mount_point, flags, password); 00761 } 00762 } 00763 } 00764 00765 return _global_ptr; 00766 } 00767 00768 #ifdef HAVE_PYTHON 00769 //////////////////////////////////////////////////////////////////// 00770 // Function: VirtualFileSystem::__py__read_file 00771 // Access: Published 00772 // Description: Convenience function; returns the entire contents of 00773 // the indicated file as a string. 00774 // 00775 // This variant on read_file() is implemented directly 00776 // for Python, as a small optimization, to avoid the 00777 // double-construction of a string object that would be 00778 // otherwise required for the return value. 00779 //////////////////////////////////////////////////////////////////// 00780 PyObject *VirtualFileSystem:: 00781 __py__read_file(const Filename &filename, bool auto_unwrap) const { 00782 pvector<unsigned char> pv; 00783 bool okflag = read_file(filename, pv, auto_unwrap); 00784 nassertr(okflag, NULL); 00785 00786 if (pv.empty()) { 00787 return PyString_FromStringAndSize("", 0); 00788 } else { 00789 return PyString_FromStringAndSize((const char *)&pv[0], pv.size()); 00790 } 00791 } 00792 #endif // HAVE_PYTHON 00793 00794 //////////////////////////////////////////////////////////////////// 00795 // Function: VirtualFileSystem::open_read_file 00796 // Access: Published 00797 // Description: Convenience function; returns a newly allocated 00798 // istream if the file exists and can be read, or NULL 00799 // otherwise. Does not return an invalid istream. 00800 // 00801 // If auto_unwrap is true, an explicitly-named .pz file 00802 // is automatically decompressed and the decompressed 00803 // contents are returned. This is different than 00804 // vfs-implicit-pz, which will automatically decompress 00805 // a file if the extension .pz is *not* given. 00806 //////////////////////////////////////////////////////////////////// 00807 istream *VirtualFileSystem:: 00808 open_read_file(const Filename &filename, bool auto_unwrap) const { 00809 PT(VirtualFile) file = get_file(filename, false); 00810 if (file == (VirtualFile *)NULL) { 00811 return NULL; 00812 } 00813 istream *str = file->open_read_file(auto_unwrap); 00814 if (str != (istream *)NULL && str->fail()) { 00815 close_read_file(str); 00816 str = (istream *)NULL; 00817 } 00818 return str; 00819 } 00820 00821 //////////////////////////////////////////////////////////////////// 00822 // Function: VirtualFileSystem::close_read_file 00823 // Access: Published, Static 00824 // Description: Closes a file opened by a previous call to 00825 // open_read_file(). This really just deletes the 00826 // istream pointer, but it is recommended to use this 00827 // interface instead of deleting it explicitly, to help 00828 // work around compiler issues. 00829 //////////////////////////////////////////////////////////////////// 00830 void VirtualFileSystem:: 00831 close_read_file(istream *stream) { 00832 if (stream != (istream *)NULL) { 00833 // For some reason--compiler bug in gcc 3.2?--explicitly deleting 00834 // the stream pointer does not call the appropriate global delete 00835 // function; instead apparently calling the system delete 00836 // function. So we call the delete function by hand instead. 00837 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW) 00838 stream->~istream(); 00839 (*global_operator_delete)(stream); 00840 #else 00841 delete stream; 00842 #endif 00843 } 00844 } 00845 00846 //////////////////////////////////////////////////////////////////// 00847 // Function: VirtualFileSystem::scan_mount_points 00848 // Access: Public 00849 // Description: Adds to names a list of all the mount points in use 00850 // that are one directory below path, if any. That is, 00851 // these are the external files or directories mounted 00852 // directly to the indicated path. 00853 // 00854 // The names vector is filled with a set of basenames, 00855 // the basename part of the mount point. 00856 //////////////////////////////////////////////////////////////////// 00857 void VirtualFileSystem:: 00858 scan_mount_points(vector_string &names, const Filename &path) const { 00859 nassertv(!path.empty() && !path.is_local()); 00860 string prefix = path.get_fullpath().substr(1); 00861 Mounts::const_iterator mi; 00862 for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) { 00863 VirtualFileMount *mount = (*mi); 00864 00865 string mount_point = mount->get_mount_point(); 00866 if (prefix.empty()) { 00867 // The indicated path is the root. Is the mount point on the 00868 // root? 00869 if (mount_point.find('/') == string::npos) { 00870 // No embedded slashes, so the mount point is only one 00871 // directory below the root. 00872 names.push_back(mount_point); 00873 } 00874 } else { 00875 if (mount_point.substr(0, prefix.length()) == prefix && 00876 mount_point.length() > prefix.length() && 00877 mount_point[prefix.length()] == '/') { 00878 // This mount point is below the indicated path. Is it only one 00879 // directory below? 00880 string basename = mount_point.substr(prefix.length()); 00881 if (basename.find('/') == string::npos) { 00882 // No embedded slashes, so it's only one directory below. 00883 names.push_back(basename); 00884 } 00885 } 00886 } 00887 } 00888 } 00889 00890 //////////////////////////////////////////////////////////////////// 00891 // Function: VirtualFileSystem::parse_option 00892 // Access: Public, Static 00893 // Description: Parses one of the option flags in the options list on 00894 // the vfs-mount Config.prc line. 00895 //////////////////////////////////////////////////////////////////// 00896 void VirtualFileSystem:: 00897 parse_option(const string &option, int &flags, string &password) { 00898 if (option == "0" || option.empty()) { 00899 // 0 is the null option. 00900 } else if (option == "ro") { 00901 flags |= MF_read_only; 00902 } else if (option.substr(0, 3) == "pw:") { 00903 password = option.substr(3); 00904 } else { 00905 express_cat.warning() 00906 << "Invalid option on vfs-mount: \"" << option << "\"\n"; 00907 } 00908 } 00909 00910 //////////////////////////////////////////////////////////////////// 00911 // Function: VirtualFileSystem::normalize_mount_point 00912 // Access: Private 00913 // Description: Converts the mount point string supplied by the user 00914 // to standard form (relative to the current directory, 00915 // with no double slashes, and not terminating with a 00916 // slash). The initial slash is removed. 00917 // 00918 // Assumes the lock is already held. 00919 //////////////////////////////////////////////////////////////////// 00920 Filename VirtualFileSystem:: 00921 normalize_mount_point(const Filename &mount_point) const { 00922 Filename nmp = mount_point; 00923 if (nmp.is_local()) { 00924 nmp = Filename(_cwd, mount_point); 00925 } 00926 nmp.standardize(); 00927 nassertr(!nmp.empty() && nmp[0] == '/', nmp); 00928 return nmp.get_fullpath().substr(1); 00929 } 00930 00931 //////////////////////////////////////////////////////////////////// 00932 // Function: VirtualFileSystem::do_mount 00933 // Access: Private 00934 // Description: The private implementation of mount(). Assumes the 00935 // lock is already held. 00936 //////////////////////////////////////////////////////////////////// 00937 bool VirtualFileSystem:: 00938 do_mount(VirtualFileMount *mount, const Filename &mount_point, int flags) { 00939 nassertr(mount->_file_system == NULL, false); 00940 mount->_file_system = this; 00941 mount->_mount_point = normalize_mount_point(mount_point); 00942 mount->_mount_flags = flags; 00943 _mounts.push_back(mount); 00944 ++_mount_seq; 00945 return true; 00946 } 00947 00948 //////////////////////////////////////////////////////////////////// 00949 // Function: VirtualFileSystem::do_get_file 00950 // Access: Private 00951 // Description: The private implementation of get_file(). Assumes 00952 // the lock is already held. 00953 //////////////////////////////////////////////////////////////////// 00954 PT(VirtualFile) VirtualFileSystem:: 00955 do_get_file(const Filename &filename, bool status_only) const { 00956 nassertr(!filename.empty(), NULL); 00957 Filename pathname(filename); 00958 if (pathname.is_local()) { 00959 pathname = Filename(_cwd, filename); 00960 if (filename.is_text()) { 00961 pathname.set_text(); 00962 } 00963 } 00964 pathname.standardize(); 00965 Filename strpath = pathname.get_filename_index(0).get_fullpath().substr(1); 00966 // Also transparently look for a regular file suffixed .pz. 00967 Filename strpath_pz = strpath + ".pz"; 00968 00969 // Now scan all the mount points, from the back (since later mounts 00970 // override more recent ones), until a match is found. 00971 PT(VirtualFile) found_file = NULL; 00972 VirtualFileComposite *composite_file = NULL; 00973 00974 // We use an index instead of an iterator, since the vector might 00975 // change if implicit mounts are added during this loop. 00976 unsigned int start_seq = _mount_seq; 00977 00978 size_t i = _mounts.size(); 00979 while (i > 0) { 00980 --i; 00981 VirtualFileMount *mount = _mounts[i]; 00982 Filename mount_point = mount->get_mount_point(); 00983 if (strpath == mount_point) { 00984 // Here's an exact match on the mount point. This filename is 00985 // the root directory of this mount object. 00986 if (consider_match(found_file, composite_file, mount, "", pathname, 00987 false, status_only)) { 00988 return found_file; 00989 } 00990 } else if (mount_point.empty()) { 00991 // This is the root mount point; all files are in here. 00992 if (consider_match(found_file, composite_file, mount, strpath, 00993 pathname, false, status_only)) { 00994 return found_file; 00995 } 00996 #ifdef HAVE_ZLIB 00997 if (vfs_implicit_pz) { 00998 if (consider_match(found_file, composite_file, mount, strpath_pz, 00999 pathname, true, status_only)) { 01000 return found_file; 01001 } 01002 } 01003 #endif // HAVE_ZLIB 01004 01005 } else if (strpath.length() > mount_point.length() && 01006 mount_point == strpath.substr(0, mount_point.length()) && 01007 strpath[mount_point.length()] == '/') { 01008 // This pathname falls within this mount system. 01009 Filename local_filename = strpath.substr(mount_point.length() + 1); 01010 Filename local_filename_pz = strpath_pz.substr(mount_point.length() + 1); 01011 if (consider_match(found_file, composite_file, mount, local_filename, 01012 pathname, false, status_only)) { 01013 return found_file; 01014 } 01015 #ifdef HAVE_ZLIB 01016 if (vfs_implicit_pz) { 01017 // Bingo! 01018 if (consider_match(found_file, composite_file, mount, local_filename_pz, 01019 pathname, true, status_only)) { 01020 return found_file; 01021 } 01022 } 01023 #endif // HAVE_ZLIB 01024 } 01025 01026 // If we discover that a file has been implicitly mounted during 01027 // one of the above operations, start over from the beginning of 01028 // the loop. 01029 if (start_seq != _mount_seq) { 01030 start_seq = _mount_seq; 01031 i = _mounts.size(); 01032 } 01033 } 01034 01035 if (found_file == (VirtualFile *)NULL && vfs_implicit_mf) { 01036 // The file wasn't found, as-is. Does it appear to be an implicit 01037 // .mf file reference? 01038 ((VirtualFileSystem *)this)->consider_mount_mf(filename); 01039 01040 if (start_seq != _mount_seq) { 01041 // Yes, it was, or some nested file was. Now that we've 01042 // implicitly mounted the .mf file, go back and look again. 01043 return do_get_file(filename, status_only); 01044 } 01045 } 01046 01047 return found_file; 01048 } 01049 01050 //////////////////////////////////////////////////////////////////// 01051 // Function: VirtualFileSystem::consider_match 01052 // Access: Private 01053 // Description: Evaluates one possible filename match found during a 01054 // get_file() operation. There may be multiple matches 01055 // for a particular filename due to the ambiguities 01056 // introduced by allowing multiple mount points, so we 01057 // may have to keep searching even after the first match 01058 // is found. 01059 // 01060 // Returns true if the search should terminate now, or 01061 // false if it should keep iterating. 01062 //////////////////////////////////////////////////////////////////// 01063 bool VirtualFileSystem:: 01064 consider_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_file, 01065 VirtualFileMount *mount, const Filename &local_filename, 01066 const Filename &original_filename, bool implicit_pz_file, 01067 bool status_only) const { 01068 PT(VirtualFile) vfile = 01069 mount->make_virtual_file(local_filename, original_filename, false, status_only); 01070 if (!vfile->has_file()) { 01071 // Keep looking. 01072 return false; 01073 } 01074 01075 if (found_file == (VirtualFile *)NULL) { 01076 // This was our first match. Save it. 01077 found_file = vfile; 01078 if (!found_file->is_directory()) { 01079 // If it's not a directory, we're done. 01080 return true; 01081 } 01082 // It is a directory, so save it for later. 01083 if (implicit_pz_file) { 01084 // Don't look for directories named file.pz. 01085 found_file = NULL; 01086 } 01087 01088 } else { 01089 // This was our second match. The previous match(es) must 01090 // have been directories. 01091 if (!vfile->is_directory()) { 01092 // However, this one isn't a directory. We're done. 01093 return true; 01094 } 01095 01096 if (!implicit_pz_file) { 01097 // At least two directories matched to the same path. We 01098 // need a composite directory. 01099 if (composite_file == (VirtualFileComposite *)NULL) { 01100 composite_file = 01101 new VirtualFileComposite((VirtualFileSystem *)this, found_file->get_original_filename()); 01102 composite_file->set_original_filename(original_filename); 01103 composite_file->add_component(found_file); 01104 found_file = composite_file; 01105 } 01106 01107 composite_file->add_component(vfile); 01108 } 01109 } 01110 01111 // Keep going, looking for more directories. 01112 return false; 01113 } 01114 01115 //////////////////////////////////////////////////////////////////// 01116 // Function: VirtualFileSystem::consider_mount_mf 01117 // Access: Private 01118 // Description: The indicated filename was not found. Check to see 01119 // if it is using an implicit reference to a .mf file as 01120 // a directory, that hasn't already been mounted. If it 01121 // is, mount the .mf file in-place, and return true; if 01122 // it is not, or if its .mf file is already mounted 01123 // in-place, return false. 01124 // 01125 // Assumes the lock is already held. 01126 //////////////////////////////////////////////////////////////////// 01127 bool VirtualFileSystem:: 01128 consider_mount_mf(const Filename &filename) { 01129 Filename dirname = filename.get_dirname(); 01130 if (dirname.empty() || dirname == filename) { 01131 // Reached the top directory; no .mf file references. 01132 return false; 01133 } 01134 if (is_directory(dirname)) { 01135 // Reached a real (or already-mounted) directory; no unmounted .mf 01136 // file references. 01137 return false; 01138 } 01139 if (dirname.get_extension() == "mf") { 01140 // Hey, here's a multifile reference! 01141 dirname.set_binary(); 01142 PT(VirtualFile) file = do_get_file(dirname, false); 01143 if (file == (VirtualFile *)NULL || !file->is_regular_file()) { 01144 // Oh, never mind. Not a real file. 01145 return false; 01146 } 01147 01148 PT(Multifile) multifile = new Multifile; 01149 01150 istream *stream = file->open_read_file(false); 01151 if (stream == (istream *)NULL) { 01152 // Couldn't read file. 01153 return false; 01154 } 01155 01156 // Wrap a thread-safe wrapper around that stream, so multiple 01157 // threads can safely read the multifile simultaneously. 01158 IStreamWrapper *streamw = new IStreamWrapper(stream, true); 01159 01160 if (!multifile->open_read(streamw, true)) { 01161 // Invalid multifile. 01162 return false; 01163 } 01164 01165 multifile->set_multifile_name(dirname.get_basename()); 01166 express_cat->info() 01167 << "Implicitly mounting " << dirname << "\n"; 01168 01169 PT(VirtualFileMountMultifile) new_mount = 01170 new VirtualFileMountMultifile(multifile); 01171 return do_mount(new_mount, dirname, MF_read_only); 01172 } 01173 01174 // Recurse. 01175 return consider_mount_mf(dirname); 01176 }