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 "virtualFileMountRamdisk.h" 00021 #include "virtualFileMountSystem.h" 00022 #include "streamWrapper.h" 00023 #include "dSearchPath.h" 00024 #include "dcast.h" 00025 #include "config_express.h" 00026 #include "executionEnvironment.h" 00027 #include "pset.h" 00028 00029 VirtualFileSystem *VirtualFileSystem::_global_ptr = NULL; 00030 00031 00032 //////////////////////////////////////////////////////////////////// 00033 // Function: VirtualFileSystem::Constructor 00034 // Access: Published 00035 // Description: 00036 //////////////////////////////////////////////////////////////////// 00037 VirtualFileSystem:: 00038 VirtualFileSystem() : 00039 vfs_case_sensitive 00040 ("vfs-case-sensitive", 00041 #ifdef NDEBUG 00042 false, // The default for a production build is not case-sensitive; 00043 // this avoids runtime overhead to verify case sensitivity. 00044 #else 00045 true, 00046 #endif 00047 PRC_DESC("Set this true to make the VirtualFileSystem present the native " 00048 "OS-provided filesystem as if it were a case-sensitive file " 00049 "system, even if it is not (e.g. on Windows). This variable " 00050 "has no effect if the native filesystem is already case-sensitive, " 00051 "and it has no effect on mounted multifile systems, which are " 00052 "always case-sensitive.")), 00053 vfs_implicit_pz 00054 ("vfs-implicit-pz", true, 00055 PRC_DESC("When this is true, the VirtualFileSystem will pretend a named " 00056 "file exists even if it doesn't, as long as a filename with the " 00057 "same name and the additional extension .pz does exist. In this " 00058 "case, the VirtualFileSystem will implicitly open the .pz file " 00059 "and decompress it on-the-fly.")), 00060 vfs_implicit_mf 00061 ("vfs-implicit-mf", false, 00062 PRC_DESC("When this is true, the VirtualFileSystem will automatically " 00063 "mount multifiles on-the-fly when they are used as directories. " 00064 "For instance, opening the file /c/files/foo.mf/dirname/mytex.jpg " 00065 "will implicitly retrieve a file named 'dirname/mytex.jpg' " 00066 "within the multifile /c/files/foo.mf, even if the multifile " 00067 "has not already been mounted. This makes all of your multifiles " 00068 "act like directories.")) 00069 { 00070 _cwd = "/"; 00071 _mount_seq = 0; 00072 } 00073 00074 //////////////////////////////////////////////////////////////////// 00075 // Function: VirtualFileSystem::Destructor 00076 // Access: Published 00077 // Description: 00078 //////////////////////////////////////////////////////////////////// 00079 VirtualFileSystem:: 00080 ~VirtualFileSystem() { 00081 unmount_all(); 00082 } 00083 00084 //////////////////////////////////////////////////////////////////// 00085 // Function: VirtualFileSystem::mount 00086 // Access: Published 00087 // Description: Mounts the indicated Multifile at the given mount 00088 // point. 00089 //////////////////////////////////////////////////////////////////// 00090 bool VirtualFileSystem:: 00091 mount(Multifile *multifile, const Filename &mount_point, int flags) { 00092 PT(VirtualFileMountMultifile) new_mount = 00093 new VirtualFileMountMultifile(multifile); 00094 return mount(new_mount, mount_point, flags); 00095 } 00096 00097 //////////////////////////////////////////////////////////////////// 00098 // Function: VirtualFileSystem::mount 00099 // Access: Published 00100 // Description: Mounts the indicated system file or directory at the 00101 // given mount point. If the named file is a directory, 00102 // mounts the directory. If the named file is a 00103 // Multifile, mounts it as a Multifile. Returns true on 00104 // success, false on failure. 00105 // 00106 // A given system directory may be mounted to multiple 00107 // different mount point, and the same mount point may 00108 // share multiple system directories. In the case of 00109 // ambiguities (that is, two different files with 00110 // exactly the same full pathname), the most-recently 00111 // mounted system wins. 00112 // 00113 // The filename specified as the first parameter must 00114 // refer to a real, physical filename on disk; it cannot 00115 // be a virtual file already appearing within the vfs 00116 // filespace. However, it is possible to mount such a 00117 // file; see mount_loop() for this. 00118 //// 00119 // Note that a mounted VirtualFileSystem directory is 00120 // fully case-sensitive, unlike the native Windows file 00121 // system, so you must refer to files within the virtual 00122 // file system with exactly the right case. 00123 //////////////////////////////////////////////////////////////////// 00124 bool VirtualFileSystem:: 00125 mount(const Filename &physical_filename, const Filename &mount_point, 00126 int flags, const string &password) { 00127 if (!physical_filename.exists()) { 00128 express_cat->warning() 00129 << "Attempt to mount " << physical_filename << ", not found.\n"; 00130 return false; 00131 } 00132 00133 if (physical_filename.is_directory()) { 00134 PT(VirtualFileMountSystem) new_mount = 00135 new VirtualFileMountSystem(physical_filename); 00136 return mount(new_mount, mount_point, flags); 00137 } else { 00138 // It's not a directory; it must be a Multifile. 00139 PT(Multifile) multifile = new Multifile; 00140 multifile->set_encryption_password(password); 00141 00142 // For now these are always opened read only. Maybe later we'll 00143 // support read-write on Multifiles. 00144 flags |= MF_read_only; 00145 if (!multifile->open_read(physical_filename)) { 00146 return false; 00147 } 00148 00149 return mount(multifile, mount_point, flags); 00150 } 00151 } 00152 00153 //////////////////////////////////////////////////////////////////// 00154 // Function: VirtualFileSystem::mount_loop 00155 // Access: Published 00156 // Description: This is similar to mount(), but it receives the name 00157 // of a Multifile that already appears within the 00158 // virtual file system. It can be used to mount a 00159 // Multifile that is itself hosted within a 00160 // virtually-mounted Multifile. 00161 // 00162 // This interface can also be used to mount physical 00163 // files (that appear within the virtual filespace), but 00164 // it cannot be used to mount directories. Use mount() 00165 // if you need to mount a directory. 00166 // 00167 // Note that there is additional overhead, in the form 00168 // of additional buffer copies of the data, for 00169 // recursively mounting a multifile like this. 00170 //////////////////////////////////////////////////////////////////// 00171 bool VirtualFileSystem:: 00172 mount_loop(const Filename &virtual_filename, const Filename &mount_point, 00173 int flags, const string &password) { 00174 PT(VirtualFile) file = get_file(virtual_filename, false); 00175 if (file == NULL) { 00176 express_cat->warning() 00177 << "Attempt to mount " << virtual_filename << ", not found.\n"; 00178 return false; 00179 } 00180 00181 if (file->is_directory()) { 00182 PT(VirtualFileMountSystem) new_mount = 00183 new VirtualFileMountSystem(virtual_filename); 00184 return mount(new_mount, mount_point, flags); 00185 00186 } else { 00187 // It's not a directory; it must be a Multifile. 00188 PT(Multifile) multifile = new Multifile; 00189 multifile->set_encryption_password(password); 00190 00191 // For now these are always opened read only. Maybe later we'll 00192 // support read-write on Multifiles. 00193 flags |= MF_read_only; 00194 if (!multifile->open_read(virtual_filename)) { 00195 return false; 00196 } 00197 00198 return mount(multifile, mount_point, flags); 00199 } 00200 } 00201 00202 //////////////////////////////////////////////////////////////////// 00203 // Function: VirtualFileSystem::mount 00204 // Access: Published 00205 // Description: Adds the given VirtualFileMount object to the mount 00206 // list. This is a lower-level function that the other 00207 // flavors of mount(); it requires you to create a 00208 // VirtualFileMount object specifically. 00209 //////////////////////////////////////////////////////////////////// 00210 bool VirtualFileSystem:: 00211 mount(VirtualFileMount *mount, const Filename &mount_point, int flags) { 00212 if (express_cat->is_debug()) { 00213 express_cat->debug() 00214 << "mount " << *mount << " under " << mount_point << "\n"; 00215 } 00216 00217 _lock.acquire(); 00218 bool result = do_mount(mount, mount_point, flags); 00219 _lock.release(); 00220 return result; 00221 } 00222 00223 //////////////////////////////////////////////////////////////////// 00224 // Function: VirtualFileSystem::unmount 00225 // Access: Published 00226 // Description: Unmounts all appearances of the indicated Multifile 00227 // from the file system. Returns the number of 00228 // appearances unmounted. 00229 //////////////////////////////////////////////////////////////////// 00230 int VirtualFileSystem:: 00231 unmount(Multifile *multifile) { 00232 _lock.acquire(); 00233 Mounts::iterator ri, wi; 00234 wi = ri = _mounts.begin(); 00235 while (ri != _mounts.end()) { 00236 VirtualFileMount *mount = (*ri); 00237 (*wi) = mount; 00238 00239 if (mount->is_exact_type(VirtualFileMountMultifile::get_class_type())) { 00240 VirtualFileMountMultifile *mmount = 00241 DCAST(VirtualFileMountMultifile, mount); 00242 if (mmount->get_multifile() == multifile) { 00243 // Remove this one. Don't increment wi. 00244 if (express_cat->is_debug()) { 00245 express_cat->debug() 00246 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n"; 00247 } 00248 mount->_file_system = NULL; 00249 00250 } else { 00251 // Don't remove this one. 00252 ++wi; 00253 } 00254 } else { 00255 // Don't remove this one. 00256 ++wi; 00257 } 00258 ++ri; 00259 } 00260 00261 int num_removed = _mounts.end() - wi; 00262 _mounts.erase(wi, _mounts.end()); 00263 ++_mount_seq; 00264 _lock.release(); 00265 return num_removed; 00266 } 00267 00268 //////////////////////////////////////////////////////////////////// 00269 // Function: VirtualFileSystem::unmount 00270 // Access: Published 00271 // Description: Unmounts all appearances of the indicated directory 00272 // name or multifile name from the file system. Returns 00273 // the number of appearances unmounted. 00274 //////////////////////////////////////////////////////////////////// 00275 int VirtualFileSystem:: 00276 unmount(const Filename &physical_filename) { 00277 _lock.acquire(); 00278 Mounts::iterator ri, wi; 00279 wi = ri = _mounts.begin(); 00280 while (ri != _mounts.end()) { 00281 VirtualFileMount *mount = (*ri); 00282 (*wi) = mount; 00283 00284 if (mount->is_exact_type(VirtualFileMountSystem::get_class_type())) { 00285 VirtualFileMountSystem *smount = 00286 DCAST(VirtualFileMountSystem, mount); 00287 if (smount->get_physical_filename() == physical_filename) { 00288 // Remove this one. Don't increment wi. 00289 if (express_cat->is_debug()) { 00290 express_cat->debug() 00291 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n"; 00292 } 00293 mount->_file_system = NULL; 00294 00295 } else { 00296 // Don't remove this one. 00297 ++wi; 00298 } 00299 00300 } else if (mount->is_exact_type(VirtualFileMountMultifile::get_class_type())) { 00301 VirtualFileMountMultifile *mmount = 00302 DCAST(VirtualFileMountMultifile, mount); 00303 if (mmount->get_multifile()->get_multifile_name() == physical_filename) { 00304 // Remove this one. Don't increment wi. 00305 if (express_cat->is_debug()) { 00306 express_cat->debug() 00307 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n"; 00308 } 00309 mount->_file_system = NULL; 00310 00311 } else { 00312 // Don't remove this one. 00313 ++wi; 00314 } 00315 00316 } else { 00317 // Don't remove this one. 00318 ++wi; 00319 } 00320 ++ri; 00321 } 00322 00323 int num_removed = _mounts.end() - wi; 00324 _mounts.erase(wi, _mounts.end()); 00325 ++_mount_seq; 00326 _lock.release(); 00327 return num_removed; 00328 } 00329 00330 //////////////////////////////////////////////////////////////////// 00331 // Function: VirtualFileSystem::unmount 00332 // Access: Published 00333 // Description: Unmounts the indicated VirtualFileMount object 00334 // from the file system. Returns the number of 00335 // appearances unmounted. 00336 //////////////////////////////////////////////////////////////////// 00337 int VirtualFileSystem:: 00338 unmount(VirtualFileMount *mount) { 00339 _lock.acquire(); 00340 Mounts::iterator ri, wi; 00341 wi = ri = _mounts.begin(); 00342 while (ri != _mounts.end()) { 00343 (*wi) = (*ri); 00344 if ((*ri) == mount) { 00345 // Remove this one. Don't increment wi. 00346 if (express_cat->is_debug()) { 00347 express_cat->debug() 00348 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n"; 00349 } 00350 (*ri)->_file_system = NULL; 00351 00352 } else { 00353 // Don't remove this one. 00354 ++wi; 00355 } 00356 ++ri; 00357 } 00358 00359 int num_removed = _mounts.end() - wi; 00360 _mounts.erase(wi, _mounts.end()); 00361 ++_mount_seq; 00362 _lock.release(); 00363 return num_removed; 00364 } 00365 00366 //////////////////////////////////////////////////////////////////// 00367 // Function: VirtualFileSystem::unmount_point 00368 // Access: Published 00369 // Description: Unmounts all systems attached to the given mount 00370 // point from the file system. Returns the number of 00371 // appearances unmounted. 00372 //////////////////////////////////////////////////////////////////// 00373 int VirtualFileSystem:: 00374 unmount_point(const Filename &mount_point) { 00375 _lock.acquire(); 00376 Filename nmp = normalize_mount_point(mount_point); 00377 Mounts::iterator ri, wi; 00378 wi = ri = _mounts.begin(); 00379 while (ri != _mounts.end()) { 00380 VirtualFileMount *mount = (*ri); 00381 (*wi) = mount; 00382 00383 if (mount->get_mount_point() == nmp) { 00384 // Remove this one. Don't increment wi. 00385 if (express_cat->is_debug()) { 00386 express_cat->debug() 00387 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n"; 00388 } 00389 mount->_file_system = NULL; 00390 00391 } else { 00392 // Don't remove this one. 00393 ++wi; 00394 } 00395 ++ri; 00396 } 00397 00398 int num_removed = _mounts.end() - wi; 00399 _mounts.erase(wi, _mounts.end()); 00400 ++_mount_seq; 00401 _lock.release(); 00402 return num_removed; 00403 } 00404 00405 //////////////////////////////////////////////////////////////////// 00406 // Function: VirtualFileSystem::unmount_all 00407 // Access: Published 00408 // Description: Unmounts all files from the file system. Returns the 00409 // number of systems unmounted. 00410 //////////////////////////////////////////////////////////////////// 00411 int VirtualFileSystem:: 00412 unmount_all() { 00413 _lock.acquire(); 00414 Mounts::iterator ri; 00415 for (ri = _mounts.begin(); ri != _mounts.end(); ++ri) { 00416 VirtualFileMount *mount = (*ri); 00417 if (express_cat->is_debug()) { 00418 express_cat->debug() 00419 << "unmount " << *mount << " from " << mount->get_mount_point() << "\n"; 00420 } 00421 mount->_file_system = NULL; 00422 } 00423 00424 int num_removed = _mounts.size(); 00425 _mounts.clear(); 00426 ++_mount_seq; 00427 _lock.release(); 00428 return num_removed; 00429 } 00430 00431 //////////////////////////////////////////////////////////////////// 00432 // Function: VirtualFileSystem::get_num_mounts 00433 // Access: Published 00434 // Description: Returns the number of individual mounts in the 00435 // system. 00436 //////////////////////////////////////////////////////////////////// 00437 int VirtualFileSystem:: 00438 get_num_mounts() const { 00439 ((VirtualFileSystem *)this)->_lock.acquire(); 00440 int result = _mounts.size(); 00441 ((VirtualFileSystem *)this)->_lock.release(); 00442 return result; 00443 } 00444 00445 //////////////////////////////////////////////////////////////////// 00446 // Function: VirtualFileSystem::get_mount 00447 // Access: Published 00448 // Description: Returns the nth mount in the system. 00449 //////////////////////////////////////////////////////////////////// 00450 PT(VirtualFileMount) VirtualFileSystem:: 00451 get_mount(int n) const { 00452 ((VirtualFileSystem *)this)->_lock.acquire(); 00453 nassertd(n >= 0 && n < (int)_mounts.size()) { 00454 ((VirtualFileSystem *)this)->_lock.release(); 00455 return NULL; 00456 } 00457 PT(VirtualFileMount) result = _mounts[n]; 00458 ((VirtualFileSystem *)this)->_lock.release(); 00459 return result; 00460 } 00461 00462 //////////////////////////////////////////////////////////////////// 00463 // Function: VirtualFileSystem::chdir 00464 // Access: Published 00465 // Description: Changes the current directory. This is used to 00466 // resolve relative pathnames in get_file() and/or 00467 // find_file(). Returns true if successful, false 00468 // otherwise. 00469 //////////////////////////////////////////////////////////////////// 00470 bool VirtualFileSystem:: 00471 chdir(const Filename &new_directory) { 00472 _lock.acquire(); 00473 if (new_directory == "/") { 00474 // We can always return to the root. 00475 _cwd = new_directory; 00476 _lock.release(); 00477 return true; 00478 } 00479 00480 PT(VirtualFile) file = do_get_file(new_directory, OF_status_only); 00481 if (file != (VirtualFile *)NULL && file->is_directory()) { 00482 _cwd = file->get_filename(); 00483 _lock.release(); 00484 return true; 00485 } 00486 _lock.release(); 00487 return false; 00488 } 00489 00490 //////////////////////////////////////////////////////////////////// 00491 // Function: VirtualFileSystem::get_cwd 00492 // Access: Published 00493 // Description: Returns the current directory name. See chdir(). 00494 //////////////////////////////////////////////////////////////////// 00495 Filename VirtualFileSystem:: 00496 get_cwd() const { 00497 ((VirtualFileSystem *)this)->_lock.acquire(); 00498 Filename result = _cwd; 00499 ((VirtualFileSystem *)this)->_lock.release(); 00500 return result; 00501 } 00502 00503 //////////////////////////////////////////////////////////////////// 00504 // Function: VirtualFileSystem::make_directory 00505 // Access: Published 00506 // Description: Attempts to create a directory within the file 00507 // system. Returns true on success, false on failure 00508 // (for instance, because the parent directory does not 00509 // exist, or is read-only). If the directory already 00510 // existed prior to this call, returns true. 00511 //////////////////////////////////////////////////////////////////// 00512 bool VirtualFileSystem:: 00513 make_directory(const Filename &filename) { 00514 _lock.acquire(); 00515 PT(VirtualFile) result = do_get_file(filename, OF_make_directory); 00516 _lock.release(); 00517 return result->is_directory(); 00518 } 00519 00520 //////////////////////////////////////////////////////////////////// 00521 // Function: VirtualFileSystem::make_directory_full 00522 // Access: Published 00523 // Description: Attempts to create a directory within the file 00524 // system. Will also create any intervening directories 00525 // needed. Returns true on success, false on failure. 00526 //////////////////////////////////////////////////////////////////// 00527 bool VirtualFileSystem:: 00528 make_directory_full(const Filename &filename) { 00529 _lock.acquire(); 00530 00531 // First, make sure everything up to the last path is known. We 00532 // don't care too much if any of these fail; maybe they failed 00533 // because the directory was already there. 00534 string dirname = filename; 00535 size_t slash = dirname.find('/', 1); 00536 while (slash != string::npos) { 00537 Filename component(dirname.substr(0, slash)); 00538 do_get_file(component, OF_make_directory); 00539 slash = dirname.find('/', slash + 1); 00540 } 00541 00542 // Now make the last one, and check the return value. 00543 PT(VirtualFile) result = do_get_file(filename, OF_make_directory); 00544 _lock.release(); 00545 return result->is_directory(); 00546 } 00547 00548 //////////////////////////////////////////////////////////////////// 00549 // Function: VirtualFileSystem::get_file 00550 // Access: Published 00551 // Description: Looks up the file by the indicated name in the file 00552 // system. Returns a VirtualFile pointer representing 00553 // the file if it is found, or NULL if it is not. 00554 // 00555 // If status_only is true, the file will be checked for 00556 // existence and length and so on, but the returned 00557 // file's contents cannot be read. This is an 00558 // optimization which is especially important for 00559 // certain mount types, for instance HTTP, for which 00560 // opening a file to determine its status is 00561 // substantially less expensive than opening it to read 00562 // its contents. 00563 //////////////////////////////////////////////////////////////////// 00564 PT(VirtualFile) VirtualFileSystem:: 00565 get_file(const Filename &filename, bool status_only) const { 00566 int open_flags = status_only ? 0 : OF_status_only; 00567 ((VirtualFileSystem *)this)->_lock.acquire(); 00568 PT(VirtualFile) result = do_get_file(filename, open_flags); 00569 ((VirtualFileSystem *)this)->_lock.release(); 00570 return result; 00571 } 00572 00573 //////////////////////////////////////////////////////////////////// 00574 // Function: VirtualFileSystem::create_file 00575 // Access: Published 00576 // Description: Attempts to create a file by the indicated name in 00577 // the filesystem, if possible, and returns it. If a 00578 // file by this name already exists, returns the same 00579 // thing as get_file(). If the filename is located 00580 // within a read-only directory, or the directory 00581 // doesn't exist, returns NULL. 00582 //////////////////////////////////////////////////////////////////// 00583 PT(VirtualFile) VirtualFileSystem:: 00584 create_file(const Filename &filename) { 00585 ((VirtualFileSystem *)this)->_lock.acquire(); 00586 PT(VirtualFile) result = do_get_file(filename, OF_create_file); 00587 ((VirtualFileSystem *)this)->_lock.release(); 00588 return result; 00589 } 00590 00591 //////////////////////////////////////////////////////////////////// 00592 // Function: VirtualFileSystem::find_file 00593 // Access: Published 00594 // Description: Uses the indicated search path to find the file 00595 // within the file system. Returns the first occurrence 00596 // of the file found, or NULL if the file cannot be 00597 // found. 00598 //////////////////////////////////////////////////////////////////// 00599 PT(VirtualFile) VirtualFileSystem:: 00600 find_file(const Filename &filename, const DSearchPath &searchpath, 00601 bool status_only) const { 00602 if (!filename.is_local()) { 00603 return get_file(filename, status_only); 00604 } 00605 00606 int num_directories = searchpath.get_num_directories(); 00607 for (int i = 0; i < num_directories; ++i) { 00608 Filename match(searchpath.get_directory(i), filename); 00609 if (searchpath.get_directory(i) == "." && 00610 filename.is_fully_qualified()) { 00611 // A special case for the "." directory: to avoid prefixing an 00612 // endless stream of ./ in front of files, if the filename 00613 // already has a ./ prefixed (i.e. is_fully_qualified() is 00614 // true), we don't prefix another one. 00615 match = filename; 00616 } 00617 PT(VirtualFile) found_file = get_file(match, status_only); 00618 if (found_file != (VirtualFile *)NULL) { 00619 return found_file; 00620 } 00621 } 00622 00623 return NULL; 00624 } 00625 00626 //////////////////////////////////////////////////////////////////// 00627 // Function: VirtualFileSystem::delete_file 00628 // Access: Public 00629 // Description: Attempts to delete the indicated file or directory. 00630 // This can remove a single file or an empty directory. 00631 // It will not remove a nonempty directory. Returns 00632 // true on success, false on failure. 00633 //////////////////////////////////////////////////////////////////// 00634 bool VirtualFileSystem:: 00635 delete_file(const Filename &filename) { 00636 PT(VirtualFile) file = get_file(filename, true); 00637 if (file == (VirtualFile *)NULL) { 00638 return false; 00639 } 00640 00641 return file->delete_file(); 00642 } 00643 00644 //////////////////////////////////////////////////////////////////// 00645 // Function: VirtualFileSystem::rename_file 00646 // Access: Public 00647 // Description: Attempts to move or rename the indicated file or 00648 // directory. If the original file is an ordinary file, 00649 // it will quietly replace any already-existing file in 00650 // the new filename (but not a directory). If the 00651 // original file is a directory, the new filename must 00652 // not already exist. 00653 // 00654 // If the file is a directory, the new filename must be 00655 // within the same mount point. If the file is an 00656 // ordinary file, the new filename may be anywhere; but 00657 // if it is not within the same mount point then the 00658 // rename operation is automatically performed as a 00659 // two-step copy-and-delete operation. 00660 //////////////////////////////////////////////////////////////////// 00661 bool VirtualFileSystem:: 00662 rename_file(const Filename &orig_filename, const Filename &new_filename) { 00663 _lock.acquire(); 00664 PT(VirtualFile) orig_file = do_get_file(orig_filename, OF_status_only); 00665 if (orig_file == (VirtualFile *)NULL) { 00666 _lock.release(); 00667 return false; 00668 } 00669 00670 PT(VirtualFile) new_file = do_get_file(new_filename, OF_status_only | OF_allow_nonexist); 00671 if (new_file == (VirtualFile *)NULL) { 00672 _lock.release(); 00673 return false; 00674 } 00675 00676 _lock.release(); 00677 00678 return orig_file->rename_file(new_file); 00679 } 00680 00681 //////////////////////////////////////////////////////////////////// 00682 // Function: VirtualFileSystem::copy_file 00683 // Access: Public 00684 // Description: Attempts to copy the contents of the indicated file 00685 // to the indicated file. Returns true on success, 00686 // false on failure. 00687 //////////////////////////////////////////////////////////////////// 00688 bool VirtualFileSystem:: 00689 copy_file(const Filename &orig_filename, const Filename &new_filename) { 00690 PT(VirtualFile) orig_file = get_file(orig_filename, true); 00691 if (orig_file == (VirtualFile *)NULL) { 00692 return false; 00693 } 00694 00695 PT(VirtualFile) new_file = create_file(new_filename); 00696 if (new_file == (VirtualFile *)NULL) { 00697 return false; 00698 } 00699 00700 return orig_file->copy_file(new_file); 00701 } 00702 00703 //////////////////////////////////////////////////////////////////// 00704 // Function: VirtualFileSystem::resolve_filename 00705 // Access: Public 00706 // Description: Searches the given search path for the filename. If 00707 // it is found, updates the filename to the full 00708 // pathname found and returns true; otherwise, returns 00709 // false. 00710 //////////////////////////////////////////////////////////////////// 00711 bool VirtualFileSystem:: 00712 resolve_filename(Filename &filename, 00713 const DSearchPath &searchpath, 00714 const string &default_extension) const { 00715 PT(VirtualFile) found; 00716 00717 if (filename.is_local()) { 00718 found = find_file(filename, searchpath, true); 00719 00720 if (found.is_null()) { 00721 // We didn't find it with the given extension; can we try the 00722 // default extension? 00723 if (filename.get_extension().empty() && !default_extension.empty()) { 00724 Filename try_ext = filename; 00725 try_ext.set_extension(default_extension); 00726 found = find_file(try_ext, searchpath, true); 00727 } 00728 } 00729 } else { 00730 if (exists(filename)) { 00731 // The full pathname exists. Return true. 00732 return true; 00733 } else { 00734 // The full pathname doesn't exist with the given extension; 00735 // does it exist with the default extension? 00736 if (filename.get_extension().empty() && !default_extension.empty()) { 00737 Filename try_ext = filename; 00738 try_ext.set_extension(default_extension); 00739 found = get_file(try_ext, true); 00740 } 00741 } 00742 } 00743 00744 if (!found.is_null()) { 00745 filename = found->get_original_filename(); 00746 return true; 00747 } 00748 00749 return false; 00750 } 00751 00752 //////////////////////////////////////////////////////////////////// 00753 // Function: VirtualFileSystem::find_all_files 00754 // Access: Public 00755 // Description: Searches all the directories in the search list for 00756 // the indicated file, in order. Fills up the results 00757 // list with *all* of the matching filenames found, if 00758 // any. Returns the number of matches found. 00759 // 00760 // It is the responsibility of the the caller to clear 00761 // the results list first; otherwise, the newly-found 00762 // files will be appended to the list. 00763 //////////////////////////////////////////////////////////////////// 00764 int VirtualFileSystem:: 00765 find_all_files(const Filename &filename, const DSearchPath &searchpath, 00766 DSearchPath::Results &results) const { 00767 int num_added = 0; 00768 00769 if (filename.is_local()) { 00770 int num_directories = searchpath.get_num_directories(); 00771 for (int i = 0; i < num_directories; ++i) { 00772 Filename match(searchpath.get_directory(i), filename); 00773 if (exists(match)) { 00774 if (searchpath.get_directory(i) == "." && 00775 filename.is_fully_qualified()) { 00776 // A special case for the "." directory: to avoid prefixing 00777 // an endless stream of ./ in front of files, if the 00778 // filename already has a ./ prefixed 00779 // (i.e. is_fully_fully_qualified() is true), we don't 00780 // prefix another one. 00781 results.add_file(filename); 00782 } else { 00783 results.add_file(match); 00784 } 00785 ++num_added; 00786 } 00787 } 00788 } 00789 00790 return num_added; 00791 } 00792 00793 //////////////////////////////////////////////////////////////////// 00794 // Function: VirtualFileSystem::write 00795 // Access: Published 00796 // Description: Print debugging information. 00797 // (e.g. from Python or gdb prompt). 00798 //////////////////////////////////////////////////////////////////// 00799 void VirtualFileSystem:: 00800 write(ostream &out) const { 00801 ((VirtualFileSystem *)this)->_lock.acquire(); 00802 Mounts::const_iterator mi; 00803 for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) { 00804 VirtualFileMount *mount = (*mi); 00805 mount->write(out); 00806 } 00807 ((VirtualFileSystem *)this)->_lock.release(); 00808 } 00809 00810 00811 //////////////////////////////////////////////////////////////////// 00812 // Function: VirtualFileSystem::get_global_ptr 00813 // Access: Published, Static 00814 // Description: Returns the default global VirtualFileSystem. You 00815 // may create your own personal VirtualFileSystem 00816 // objects and use them for whatever you like, but Panda 00817 // will attempt to load models and stuff from this 00818 // default object. 00819 // 00820 // Initially, the global VirtualFileSystem is set up to 00821 // mount the OS filesystem to root; i.e. it is 00822 // equivalent to the OS filesystem. This may be 00823 // subsequently adjusted by the user. 00824 //////////////////////////////////////////////////////////////////// 00825 VirtualFileSystem *VirtualFileSystem:: 00826 get_global_ptr() { 00827 if (_global_ptr == (VirtualFileSystem *)NULL) { 00828 // Make sure this is initialized. 00829 init_libexpress(); 00830 00831 _global_ptr = new VirtualFileSystem; 00832 00833 // Set up the default mounts. First, there is always the root 00834 // mount. 00835 _global_ptr->mount("/", "/", 0); 00836 00837 // And our initial cwd comes from the environment. 00838 _global_ptr->chdir(ExecutionEnvironment::get_cwd()); 00839 00840 // Then, we add whatever mounts are listed in the Configrc file. 00841 ConfigVariableList mounts 00842 ("vfs-mount", 00843 PRC_DESC("vfs-mount system-filename mount-point [options]")); 00844 00845 int num_unique_values = mounts.get_num_unique_values(); 00846 for (int i = 0; i < num_unique_values; i++) { 00847 string mount_desc = mounts.get_unique_value(i); 00848 00849 // The vfs-mount syntax is: 00850 00851 // vfs-mount system-filename mount-point [options] 00852 00853 // The last two spaces mark the beginning of the mount point, 00854 // and of the options, respectively. There might be multiple 00855 // spaces in the system filename, which are part of the 00856 // filename. 00857 00858 // The last space marks the beginning of the mount point. 00859 // Spaces before that are part of the system filename. 00860 size_t space = mount_desc.rfind(' '); 00861 if (space == string::npos) { 00862 express_cat.warning() 00863 << "No space in vfs-mount descriptor: " << mount_desc << "\n"; 00864 00865 } else { 00866 string mount_point = mount_desc.substr(space + 1); 00867 while (space > 0 && isspace(mount_desc[space - 1])) { 00868 space--; 00869 } 00870 mount_desc = mount_desc.substr(0, space); 00871 string options; 00872 00873 space = mount_desc.rfind(' '); 00874 if (space != string::npos) { 00875 // If there's another space, we have the optional options field. 00876 options = mount_point; 00877 mount_point = mount_desc.substr(space + 1); 00878 while (space > 0 && isspace(mount_desc[space - 1])) { 00879 --space; 00880 } 00881 mount_desc = mount_desc.substr(0, space); 00882 } 00883 00884 mount_desc = ExecutionEnvironment::expand_string(mount_desc); 00885 Filename physical_filename = Filename::from_os_specific(mount_desc); 00886 00887 int flags; 00888 string password; 00889 parse_options(options, flags, password); 00890 _global_ptr->mount(physical_filename, mount_point, flags, password); 00891 } 00892 } 00893 00894 ConfigVariableString vfs_mount_ramdisk 00895 ("vfs-mount-ramdisk", "", 00896 PRC_DESC("vfs-mount-ramdisk mount-point [options]")); 00897 if (!vfs_mount_ramdisk.empty()) { 00898 string mount_point = vfs_mount_ramdisk; 00899 string options; 00900 00901 size_t space = mount_point.rfind(' '); 00902 if (space != string::npos) { 00903 // If there's a space, we have the optional options field. 00904 options = mount_point.substr(space + 1); 00905 while (space > 0 && isspace(mount_point[space - 1])) { 00906 --space; 00907 } 00908 mount_point = mount_point.substr(0, space); 00909 } 00910 00911 int flags; 00912 string password; 00913 parse_options(options, flags, password); 00914 00915 PT(VirtualFileMount) ramdisk = new VirtualFileMountRamdisk; 00916 _global_ptr->mount(ramdisk, mount_point, flags); 00917 } 00918 } 00919 00920 return _global_ptr; 00921 } 00922 00923 #ifdef HAVE_PYTHON 00924 //////////////////////////////////////////////////////////////////// 00925 // Function: VirtualFileSystem::__py__read_file 00926 // Access: Published 00927 // Description: Convenience function; returns the entire contents of 00928 // the indicated file as a string. 00929 // 00930 // This variant on read_file() is implemented directly 00931 // for Python, as a small optimization, to avoid the 00932 // double-construction of a string object that would be 00933 // otherwise required for the return value. 00934 //////////////////////////////////////////////////////////////////// 00935 PyObject *VirtualFileSystem:: 00936 __py__read_file(const Filename &filename, bool auto_unwrap) const { 00937 pvector<unsigned char> pv; 00938 bool okflag = read_file(filename, pv, auto_unwrap); 00939 nassertr(okflag, NULL); 00940 00941 if (pv.empty()) { 00942 return PyString_FromStringAndSize("", 0); 00943 } else { 00944 return PyString_FromStringAndSize((const char *)&pv[0], pv.size()); 00945 } 00946 } 00947 #endif // HAVE_PYTHON 00948 00949 //////////////////////////////////////////////////////////////////// 00950 // Function: VirtualFileSystem::open_read_file 00951 // Access: Published 00952 // Description: Convenience function; returns a newly allocated 00953 // istream if the file exists and can be read, or NULL 00954 // otherwise. Does not return an invalid istream. 00955 // 00956 // If auto_unwrap is true, an explicitly-named .pz file 00957 // is automatically decompressed and the decompressed 00958 // contents are returned. This is different than 00959 // vfs-implicit-pz, which will automatically decompress 00960 // a file if the extension .pz is *not* given. 00961 //////////////////////////////////////////////////////////////////// 00962 istream *VirtualFileSystem:: 00963 open_read_file(const Filename &filename, bool auto_unwrap) const { 00964 PT(VirtualFile) file = get_file(filename, false); 00965 if (file == (VirtualFile *)NULL) { 00966 return NULL; 00967 } 00968 istream *str = file->open_read_file(auto_unwrap); 00969 if (str != (istream *)NULL && str->fail()) { 00970 close_read_file(str); 00971 str = (istream *)NULL; 00972 } 00973 return str; 00974 } 00975 00976 //////////////////////////////////////////////////////////////////// 00977 // Function: VirtualFileSystem::close_read_file 00978 // Access: Published, Static 00979 // Description: Closes a file opened by a previous call to 00980 // open_read_file(). This really just deletes the 00981 // istream pointer, but it is recommended to use this 00982 // interface instead of deleting it explicitly, to help 00983 // work around compiler issues. 00984 //////////////////////////////////////////////////////////////////// 00985 void VirtualFileSystem:: 00986 close_read_file(istream *stream) { 00987 if (stream != (istream *)NULL) { 00988 // For some reason--compiler bug in gcc 3.2?--explicitly deleting 00989 // the stream pointer does not call the appropriate global delete 00990 // function; instead apparently calling the system delete 00991 // function. So we call the delete function by hand instead. 00992 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW) 00993 stream->~istream(); 00994 (*global_operator_delete)(stream); 00995 #else 00996 delete stream; 00997 #endif 00998 } 00999 } 01000 01001 #ifdef HAVE_PYTHON 01002 //////////////////////////////////////////////////////////////////// 01003 // Function: VirtualFileSystem::__py__write_file 01004 // Access: Published 01005 // Description: Convenience function; writes the entire contents of 01006 // the indicated file as a string. 01007 // 01008 // This variant on write_file() is implemented directly 01009 // for Python, as a small optimization, to avoid the 01010 // double-construction of a string object that would be 01011 // otherwise required. 01012 //////////////////////////////////////////////////////////////////// 01013 PyObject *VirtualFileSystem:: 01014 __py__write_file(const Filename &filename, PyObject *data, bool auto_wrap) { 01015 char *buffer; 01016 Py_ssize_t length; 01017 if (PyString_AsStringAndSize(data, &buffer, &length) == -1) { 01018 return NULL; 01019 } 01020 01021 bool result = write_file(filename, (const unsigned char *)buffer, length, auto_wrap); 01022 return PyBool_FromLong(result); 01023 } 01024 #endif // HAVE_PYTHON 01025 01026 //////////////////////////////////////////////////////////////////// 01027 // Function: VirtualFileSystem::open_write_file 01028 // Access: Published 01029 // Description: Convenience function; returns a newly allocated 01030 // ostream if the file exists and can be written, or 01031 // NULL otherwise. Does not return an invalid ostream. 01032 // 01033 // If auto_wrap is true, an explicitly-named .pz file is 01034 // automatically compressed while writing. If truncate 01035 // is true, the file is truncated to zero length before 01036 // writing. 01037 //////////////////////////////////////////////////////////////////// 01038 ostream *VirtualFileSystem:: 01039 open_write_file(const Filename &filename, bool auto_wrap, bool truncate) { 01040 PT(VirtualFile) file = create_file(filename); 01041 if (file == (VirtualFile *)NULL) { 01042 return NULL; 01043 } 01044 ostream *str = file->open_write_file(auto_wrap, truncate); 01045 if (str != (ostream *)NULL && str->fail()) { 01046 close_write_file(str); 01047 str = (ostream *)NULL; 01048 } 01049 return str; 01050 } 01051 01052 //////////////////////////////////////////////////////////////////// 01053 // Function: VirtualFileSystem::open_append_file 01054 // Access: Published 01055 // Description: Works like open_write_file(), but the file is opened 01056 // in append mode. Like open_write_file, the returned 01057 // pointer should eventually be passed to 01058 // close_write_file(). 01059 //////////////////////////////////////////////////////////////////// 01060 ostream *VirtualFileSystem:: 01061 open_append_file(const Filename &filename) { 01062 PT(VirtualFile) file = create_file(filename); 01063 if (file == (VirtualFile *)NULL) { 01064 return NULL; 01065 } 01066 ostream *str = file->open_append_file(); 01067 if (str != (ostream *)NULL && str->fail()) { 01068 close_write_file(str); 01069 str = (ostream *)NULL; 01070 } 01071 return str; 01072 } 01073 01074 //////////////////////////////////////////////////////////////////// 01075 // Function: VirtualFileSystem::close_write_file 01076 // Access: Published, Static 01077 // Description: Closes a file opened by a previous call to 01078 // open_write_file(). This really just deletes the 01079 // ostream pointer, but it is recommended to use this 01080 // interface instead of deleting it explicitly, to help 01081 // work around compiler issues. 01082 //////////////////////////////////////////////////////////////////// 01083 void VirtualFileSystem:: 01084 close_write_file(ostream *stream) { 01085 if (stream != (ostream *)NULL) { 01086 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW) 01087 stream->~ostream(); 01088 (*global_operator_delete)(stream); 01089 #else 01090 delete stream; 01091 #endif 01092 } 01093 } 01094 01095 //////////////////////////////////////////////////////////////////// 01096 // Function: VirtualFileSystem::open_read_write_file 01097 // Access: Published 01098 // Description: Convenience function; returns a newly allocated 01099 // iostream if the file exists and can be written, or 01100 // NULL otherwise. Does not return an invalid iostream. 01101 //////////////////////////////////////////////////////////////////// 01102 iostream *VirtualFileSystem:: 01103 open_read_write_file(const Filename &filename, bool truncate) { 01104 PT(VirtualFile) file = create_file(filename); 01105 if (file == (VirtualFile *)NULL) { 01106 return NULL; 01107 } 01108 iostream *str = file->open_read_write_file(truncate); 01109 if (str != (iostream *)NULL && str->fail()) { 01110 close_read_write_file(str); 01111 str = (iostream *)NULL; 01112 } 01113 return str; 01114 } 01115 01116 //////////////////////////////////////////////////////////////////// 01117 // Function: VirtualFileSystem::open_read_append_file 01118 // Access: Published 01119 // Description: Works like open_read_write_file(), but the file is opened 01120 // in append mode. Like open_read_write_file, the returned 01121 // pointer should eventually be passed to 01122 // close_read_write_file(). 01123 //////////////////////////////////////////////////////////////////// 01124 iostream *VirtualFileSystem:: 01125 open_read_append_file(const Filename &filename) { 01126 PT(VirtualFile) file = create_file(filename); 01127 if (file == (VirtualFile *)NULL) { 01128 return NULL; 01129 } 01130 iostream *str = file->open_read_append_file(); 01131 if (str != (iostream *)NULL && str->fail()) { 01132 close_read_write_file(str); 01133 str = (iostream *)NULL; 01134 } 01135 return str; 01136 } 01137 01138 //////////////////////////////////////////////////////////////////// 01139 // Function: VirtualFileSystem::close_read_write_file 01140 // Access: Published, Static 01141 // Description: Closes a file opened by a previous call to 01142 // open_read_write_file(). This really just deletes the 01143 // iostream pointer, but it is recommended to use this 01144 // interface instead of deleting it explicitly, to help 01145 // work around compiler issues. 01146 //////////////////////////////////////////////////////////////////// 01147 void VirtualFileSystem:: 01148 close_read_write_file(iostream *stream) { 01149 if (stream != (iostream *)NULL) { 01150 #if (!defined(WIN32_VC) && !defined(WIN64_VC)) && !defined(USE_MEMORY_NOWRAPPERS) && defined(REDEFINE_GLOBAL_OPERATOR_NEW) 01151 stream->~iostream(); 01152 (*global_operator_delete)(stream); 01153 #else 01154 delete stream; 01155 #endif 01156 } 01157 } 01158 01159 //////////////////////////////////////////////////////////////////// 01160 // Function: VirtualFileSystem::atomic_compare_and_exchange_contents 01161 // Access: Public 01162 // Description: See Filename::atomic_compare_and_exchange_contents(). 01163 //////////////////////////////////////////////////////////////////// 01164 bool VirtualFileSystem:: 01165 atomic_compare_and_exchange_contents(const Filename &filename, string &orig_contents, 01166 const string &old_contents, 01167 const string &new_contents) { 01168 PT(VirtualFile) file = create_file(filename); 01169 if (file == NULL) { 01170 return false; 01171 } 01172 01173 return file->atomic_compare_and_exchange_contents(orig_contents, old_contents, new_contents); 01174 } 01175 01176 //////////////////////////////////////////////////////////////////// 01177 // Function: VirtualFileSystem::atomic_read_contents 01178 // Access: Public 01179 // Description: See Filename::atomic_read_contents(). 01180 //////////////////////////////////////////////////////////////////// 01181 bool VirtualFileSystem:: 01182 atomic_read_contents(const Filename &filename, string &contents) const { 01183 PT(VirtualFile) file = get_file(filename, false); 01184 if (file == NULL) { 01185 return false; 01186 } 01187 01188 return file->atomic_read_contents(contents); 01189 } 01190 01191 //////////////////////////////////////////////////////////////////// 01192 // Function: VirtualFileSystem::scan_mount_points 01193 // Access: Public 01194 // Description: Adds to names a list of all the mount points in use 01195 // that are one directory below path, if any. That is, 01196 // these are the external files or directories mounted 01197 // directly to the indicated path. 01198 // 01199 // The names vector is filled with a set of basenames, 01200 // the basename part of the mount point. 01201 //////////////////////////////////////////////////////////////////// 01202 void VirtualFileSystem:: 01203 scan_mount_points(vector_string &names, const Filename &path) const { 01204 nassertv(!path.empty() && !path.is_local()); 01205 string prefix = path.get_fullpath().substr(1); 01206 Mounts::const_iterator mi; 01207 for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) { 01208 VirtualFileMount *mount = (*mi); 01209 01210 string mount_point = mount->get_mount_point(); 01211 if (prefix.empty()) { 01212 // The indicated path is the root. Is the mount point on the 01213 // root? 01214 if (mount_point.find('/') == string::npos) { 01215 // No embedded slashes, so the mount point is only one 01216 // directory below the root. 01217 names.push_back(mount_point); 01218 } 01219 } else { 01220 if (mount_point.substr(0, prefix.length()) == prefix && 01221 mount_point.length() > prefix.length() && 01222 mount_point[prefix.length()] == '/') { 01223 // This mount point is below the indicated path. Is it only one 01224 // directory below? 01225 string basename = mount_point.substr(prefix.length()); 01226 if (basename.find('/') == string::npos) { 01227 // No embedded slashes, so it's only one directory below. 01228 names.push_back(basename); 01229 } 01230 } 01231 } 01232 } 01233 } 01234 01235 01236 //////////////////////////////////////////////////////////////////// 01237 // Function: VirtualFileSystem::parse_options 01238 // Access: Public, Static 01239 // Description: Parses all of the option flags in the options list on 01240 // the vfs-mount Config.prc line. 01241 //////////////////////////////////////////////////////////////////// 01242 void VirtualFileSystem:: 01243 parse_options(const string &options, int &flags, string &password) { 01244 flags = 0; 01245 password = string(); 01246 01247 // Split the options up by commas. 01248 size_t p = 0; 01249 size_t q = options.find(',', p); 01250 while (q != string::npos) { 01251 parse_option(options.substr(p, q - p), 01252 flags, password); 01253 p = q + 1; 01254 q = options.find(',', p); 01255 } 01256 parse_option(options.substr(p), flags, password); 01257 } 01258 01259 //////////////////////////////////////////////////////////////////// 01260 // Function: VirtualFileSystem::parse_option 01261 // Access: Public, Static 01262 // Description: Parses one of the option flags in the options list on 01263 // the vfs-mount Config.prc line. 01264 //////////////////////////////////////////////////////////////////// 01265 void VirtualFileSystem:: 01266 parse_option(const string &option, int &flags, string &password) { 01267 if (option == "0" || option.empty()) { 01268 // 0 is the null option. 01269 } else if (option == "ro") { 01270 flags |= MF_read_only; 01271 } else if (option.substr(0, 3) == "pw:") { 01272 password = option.substr(3); 01273 } else { 01274 express_cat.warning() 01275 << "Invalid option on vfs-mount: \"" << option << "\"\n"; 01276 } 01277 } 01278 01279 //////////////////////////////////////////////////////////////////// 01280 // Function: VirtualFileSystem::normalize_mount_point 01281 // Access: Private 01282 // Description: Converts the mount point string supplied by the user 01283 // to standard form (relative to the current directory, 01284 // with no double slashes, and not terminating with a 01285 // slash). The initial slash is removed. 01286 // 01287 // Assumes the lock is already held. 01288 //////////////////////////////////////////////////////////////////// 01289 Filename VirtualFileSystem:: 01290 normalize_mount_point(const Filename &mount_point) const { 01291 Filename nmp = mount_point; 01292 if (nmp.is_local()) { 01293 nmp = Filename(_cwd, mount_point); 01294 } 01295 nmp.standardize(); 01296 nassertr(!nmp.empty() && nmp[0] == '/', nmp); 01297 return nmp.get_fullpath().substr(1); 01298 } 01299 01300 //////////////////////////////////////////////////////////////////// 01301 // Function: VirtualFileSystem::do_mount 01302 // Access: Private 01303 // Description: The private implementation of mount(). Assumes the 01304 // lock is already held. 01305 //////////////////////////////////////////////////////////////////// 01306 bool VirtualFileSystem:: 01307 do_mount(VirtualFileMount *mount, const Filename &mount_point, int flags) { 01308 nassertr(mount->_file_system == NULL, false); 01309 mount->_file_system = this; 01310 mount->_mount_point = normalize_mount_point(mount_point); 01311 mount->_mount_flags = flags; 01312 _mounts.push_back(mount); 01313 ++_mount_seq; 01314 return true; 01315 } 01316 01317 //////////////////////////////////////////////////////////////////// 01318 // Function: VirtualFileSystem::do_get_file 01319 // Access: Private 01320 // Description: The private implementation of get_file(), 01321 // create_file(), and make_directory(). Assumes the 01322 // lock is already held. 01323 //////////////////////////////////////////////////////////////////// 01324 PT(VirtualFile) VirtualFileSystem:: 01325 do_get_file(const Filename &filename, int open_flags) const { 01326 if (filename.empty()) { 01327 return NULL; 01328 } 01329 Filename pathname(filename); 01330 if (pathname.is_local()) { 01331 pathname = Filename(_cwd, filename); 01332 if (filename.is_text()) { 01333 pathname.set_text(); 01334 } 01335 } 01336 pathname.standardize(); 01337 Filename strpath = pathname.get_filename_index(0).get_fullpath().substr(1); 01338 strpath.set_type(filename.get_type()); 01339 // Also transparently look for a regular file suffixed .pz. 01340 Filename strpath_pz = strpath + ".pz"; 01341 01342 // Now scan all the mount points, from the back (since later mounts 01343 // override more recent ones), until a match is found. 01344 PT(VirtualFile) found_file = NULL; 01345 VirtualFileComposite *composite_file = NULL; 01346 01347 // We use an index instead of an iterator, since the vector might 01348 // change if implicit mounts are added during this loop. 01349 unsigned int start_seq = _mount_seq; 01350 01351 size_t i = _mounts.size(); 01352 while (i > 0) { 01353 --i; 01354 VirtualFileMount *mount = _mounts[i]; 01355 Filename mount_point = mount->get_mount_point(); 01356 if (strpath == mount_point) { 01357 // Here's an exact match on the mount point. This filename is 01358 // the root directory of this mount object. 01359 if (consider_match(found_file, composite_file, mount, "", pathname, 01360 false, open_flags)) { 01361 return found_file; 01362 } 01363 } else if (mount_point.empty()) { 01364 // This is the root mount point; all files are in here. 01365 if (consider_match(found_file, composite_file, mount, strpath, 01366 pathname, false, open_flags)) { 01367 return found_file; 01368 } 01369 #ifdef HAVE_ZLIB 01370 if (vfs_implicit_pz) { 01371 if (consider_match(found_file, composite_file, mount, strpath_pz, 01372 pathname, true, open_flags)) { 01373 return found_file; 01374 } 01375 } 01376 #endif // HAVE_ZLIB 01377 01378 } else if (strpath.length() > mount_point.length() && 01379 mount_point == strpath.substr(0, mount_point.length()) && 01380 strpath[mount_point.length()] == '/') { 01381 // This pathname falls within this mount system. 01382 Filename local_filename = strpath.substr(mount_point.length() + 1); 01383 Filename local_filename_pz = strpath_pz.substr(mount_point.length() + 1); 01384 if (consider_match(found_file, composite_file, mount, local_filename, 01385 pathname, false, open_flags)) { 01386 return found_file; 01387 } 01388 #ifdef HAVE_ZLIB 01389 if (vfs_implicit_pz) { 01390 // Bingo! 01391 if (consider_match(found_file, composite_file, mount, local_filename_pz, 01392 pathname, true, open_flags)) { 01393 return found_file; 01394 } 01395 } 01396 #endif // HAVE_ZLIB 01397 } 01398 01399 // If we discover that a file has been implicitly mounted during 01400 // one of the above operations, start over from the beginning of 01401 // the loop. 01402 if (start_seq != _mount_seq) { 01403 start_seq = _mount_seq; 01404 i = _mounts.size(); 01405 } 01406 } 01407 01408 if (found_file == (VirtualFile *)NULL && vfs_implicit_mf) { 01409 // The file wasn't found, as-is. Does it appear to be an implicit 01410 // .mf file reference? 01411 ((VirtualFileSystem *)this)->consider_mount_mf(filename); 01412 01413 if (start_seq != _mount_seq) { 01414 // Yes, it was, or some nested file was. Now that we've 01415 // implicitly mounted the .mf file, go back and look again. 01416 return do_get_file(filename, open_flags); 01417 } 01418 } 01419 01420 return found_file; 01421 } 01422 01423 //////////////////////////////////////////////////////////////////// 01424 // Function: VirtualFileSystem::consider_match 01425 // Access: Private 01426 // Description: Evaluates one possible filename match found during a 01427 // get_file() operation. There may be multiple matches 01428 // for a particular filename due to the ambiguities 01429 // introduced by allowing multiple mount points, so we 01430 // may have to keep searching even after the first match 01431 // is found. 01432 // 01433 // Returns true if the search should terminate now, or 01434 // false if it should keep iterating. 01435 //////////////////////////////////////////////////////////////////// 01436 bool VirtualFileSystem:: 01437 consider_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_file, 01438 VirtualFileMount *mount, const Filename &local_filename, 01439 const Filename &original_filename, bool implicit_pz_file, 01440 int open_flags) const { 01441 PT(VirtualFile) vfile = 01442 mount->make_virtual_file(local_filename, original_filename, false, open_flags); 01443 if (!vfile->has_file() && ((open_flags & OF_allow_nonexist) == 0)) { 01444 // Keep looking. 01445 return false; 01446 } 01447 01448 if (found_file == (VirtualFile *)NULL) { 01449 // This was our first match. Save it. 01450 found_file = vfile; 01451 if (!found_file->is_directory() || ((open_flags & OF_make_directory) != 0)) { 01452 // If it's not a directory (or we wanted to make a directory), we're done. 01453 return true; 01454 } 01455 // It is a directory, so save it for later. 01456 if (implicit_pz_file) { 01457 // Don't look for directories named file.pz. 01458 found_file = NULL; 01459 } 01460 01461 } else { 01462 // This was our second match. The previous match(es) must 01463 // have been directories. 01464 if (!vfile->is_directory()) { 01465 // However, this one isn't a directory. We're done. 01466 return true; 01467 } 01468 01469 if (!implicit_pz_file) { 01470 // At least two directories matched to the same path. We 01471 // need a composite directory. 01472 if (composite_file == (VirtualFileComposite *)NULL) { 01473 composite_file = 01474 new VirtualFileComposite((VirtualFileSystem *)this, found_file->get_original_filename()); 01475 composite_file->set_original_filename(original_filename); 01476 composite_file->add_component(found_file); 01477 found_file = composite_file; 01478 } 01479 01480 composite_file->add_component(vfile); 01481 } 01482 } 01483 01484 // Keep going, looking for more directories. 01485 return false; 01486 } 01487 01488 //////////////////////////////////////////////////////////////////// 01489 // Function: VirtualFileSystem::consider_mount_mf 01490 // Access: Private 01491 // Description: The indicated filename was not found. Check to see 01492 // if it is using an implicit reference to a .mf file as 01493 // a directory, that hasn't already been mounted. If it 01494 // is, mount the .mf file in-place, and return true; if 01495 // it is not, or if its .mf file is already mounted 01496 // in-place, return false. 01497 // 01498 // Assumes the lock is already held. 01499 //////////////////////////////////////////////////////////////////// 01500 bool VirtualFileSystem:: 01501 consider_mount_mf(const Filename &filename) { 01502 Filename dirname = filename.get_dirname(); 01503 if (dirname.empty() || dirname == filename) { 01504 // Reached the top directory; no .mf file references. 01505 return false; 01506 } 01507 if (is_directory(dirname)) { 01508 // Reached a real (or already-mounted) directory; no unmounted .mf 01509 // file references. 01510 return false; 01511 } 01512 if (dirname.get_extension() == "mf") { 01513 // Hey, here's a multifile reference! 01514 dirname.set_binary(); 01515 PT(VirtualFile) file = do_get_file(dirname, false); 01516 if (file == (VirtualFile *)NULL || !file->is_regular_file()) { 01517 // Oh, never mind. Not a real file. 01518 return false; 01519 } 01520 01521 PT(Multifile) multifile = new Multifile; 01522 01523 istream *stream = file->open_read_file(false); 01524 if (stream == (istream *)NULL) { 01525 // Couldn't read file. 01526 return false; 01527 } 01528 01529 // Wrap a thread-safe wrapper around that stream, so multiple 01530 // threads can safely read the multifile simultaneously. 01531 IStreamWrapper *streamw = new IStreamWrapper(stream, true); 01532 01533 if (!multifile->open_read(streamw, true)) { 01534 // Invalid multifile. 01535 return false; 01536 } 01537 01538 multifile->set_multifile_name(dirname.get_basename()); 01539 express_cat->info() 01540 << "Implicitly mounting " << dirname << "\n"; 01541 01542 PT(VirtualFileMountMultifile) new_mount = 01543 new VirtualFileMountMultifile(multifile); 01544 return do_mount(new_mount, dirname, MF_read_only); 01545 } 01546 01547 // Recurse. 01548 return consider_mount_mf(dirname); 01549 }