virtualFileSystem.cxx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. // Filename: virtualFileSystem.cxx
  2. // Created by: drose (03Aug02)
  3. //
  4. ////////////////////////////////////////////////////////////////////
  5. //
  6. // PANDA 3D SOFTWARE
  7. // Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved
  8. //
  9. // All use of this software is subject to the terms of the Panda 3d
  10. // Software license. You should have received a copy of this license
  11. // along with this source code; you will also find a current copy of
  12. // the license at http://www.panda3d.org/license.txt .
  13. //
  14. // To contact the maintainers of this program write to
  15. // [email protected] .
  16. //
  17. ////////////////////////////////////////////////////////////////////
  18. #include "virtualFileSystem.h"
  19. #include "virtualFileMount.h"
  20. #include "virtualFileMountMultifile.h"
  21. #include "virtualFileMountSystem.h"
  22. #include "dSearchPath.h"
  23. #include "dcast.h"
  24. #include "config_util.h"
  25. #include "executionEnvironment.h"
  26. VirtualFileSystem *VirtualFileSystem::_global_ptr = NULL;
  27. ////////////////////////////////////////////////////////////////////
  28. // Function: VirtualFileSystem::Constructor
  29. // Access: Published
  30. // Description:
  31. ////////////////////////////////////////////////////////////////////
  32. VirtualFileSystem::
  33. VirtualFileSystem() {
  34. _cwd = "/";
  35. }
  36. ////////////////////////////////////////////////////////////////////
  37. // Function: VirtualFileSystem::Destructor
  38. // Access: Published
  39. // Description:
  40. ////////////////////////////////////////////////////////////////////
  41. VirtualFileSystem::
  42. ~VirtualFileSystem() {
  43. unmount_all();
  44. }
  45. ////////////////////////////////////////////////////////////////////
  46. // Function: VirtualFileSystem::mount
  47. // Access: Published
  48. // Description: Mounts the indicated Multifile at the given mount
  49. // point. If flags contains MF_owns_pointer, the
  50. // Multifile will be deleted when it is eventually
  51. // unmounted.
  52. ////////////////////////////////////////////////////////////////////
  53. bool VirtualFileSystem::
  54. mount(Multifile *multifile, const string &mount_point, int flags) {
  55. VirtualFileMountMultifile *mount =
  56. new VirtualFileMountMultifile(this, multifile,
  57. normalize_mount_point(mount_point),
  58. flags);
  59. _mounts.push_back(mount);
  60. return true;
  61. }
  62. ////////////////////////////////////////////////////////////////////
  63. // Function: VirtualFileSystem::mount
  64. // Access: Published
  65. // Description: Mounts the indicated system file or directory at the
  66. // given mount point. If the named file is a directory,
  67. // mounts the directory. If the named file is a
  68. // Multifile, mounts it as a Multifile. Returns true on
  69. // success, false on failure.
  70. //
  71. // A given system directory may be mounted to multiple
  72. // different mount point, and the same mount point may
  73. // share multiple system directories. In the case of
  74. // ambiguities, the most-recently mounted system wins.
  75. ////////////////////////////////////////////////////////////////////
  76. bool VirtualFileSystem::
  77. mount(const Filename &physical_filename, const string &mount_point,
  78. int flags) {
  79. if (!physical_filename.exists()) {
  80. util_cat.warning()
  81. << "Attempt to mount " << physical_filename << ", not found.\n";
  82. return false;
  83. }
  84. if (physical_filename.is_directory()) {
  85. flags &= ~MF_owns_pointer;
  86. VirtualFileMountSystem *mount =
  87. new VirtualFileMountSystem(this, physical_filename,
  88. normalize_mount_point(mount_point),
  89. flags);
  90. _mounts.push_back(mount);
  91. return true;
  92. } else {
  93. // It's not a directory; it must be a Multifile.
  94. Multifile *multifile = new Multifile;
  95. // For now these are always opened read only. Maybe later we'll
  96. // support read-write on Multifiles.
  97. flags |= MF_read_only;
  98. if (!multifile->open_read(physical_filename)) {
  99. delete multifile;
  100. return false;
  101. }
  102. // We want to delete this pointer when we're done.
  103. flags |= MF_owns_pointer;
  104. return mount(multifile, mount_point, flags);
  105. }
  106. }
  107. ////////////////////////////////////////////////////////////////////
  108. // Function: VirtualFileSystem::unmount
  109. // Access: Published
  110. // Description: Unmounts all appearances of the indicated Multifile
  111. // from the file system. Returns the number of
  112. // appearances unmounted.
  113. ////////////////////////////////////////////////////////////////////
  114. int VirtualFileSystem::
  115. unmount(Multifile *multifile) {
  116. Mounts::iterator ri, wi;
  117. wi = ri = _mounts.begin();
  118. while (ri != _mounts.end()) {
  119. VirtualFileMount *mount = (*ri);
  120. (*wi) = mount;
  121. if (mount->is_exact_type(VirtualFileMountMultifile::get_class_type())) {
  122. VirtualFileMountMultifile *mmount =
  123. DCAST(VirtualFileMountMultifile, mount);
  124. if (mmount->get_multifile() == multifile) {
  125. // Remove this one. Don't increment wi.
  126. delete mount;
  127. } else {
  128. // Don't remove this one.
  129. ++wi;
  130. }
  131. } else {
  132. // Don't remove this one.
  133. ++wi;
  134. }
  135. ++ri;
  136. }
  137. int num_removed = _mounts.end() - wi;
  138. _mounts.erase(wi, _mounts.end());
  139. return num_removed;
  140. }
  141. ////////////////////////////////////////////////////////////////////
  142. // Function: VirtualFileSystem::unmount
  143. // Access: Published
  144. // Description: Unmounts all appearances of the indicated physical
  145. // filename (either a directory name or a Multifile
  146. // name) from the file system. Returns the number of
  147. // appearances unmounted.
  148. ////////////////////////////////////////////////////////////////////
  149. int VirtualFileSystem::
  150. unmount(const Filename &physical_filename) {
  151. Mounts::iterator ri, wi;
  152. wi = ri = _mounts.begin();
  153. while (ri != _mounts.end()) {
  154. VirtualFileMount *mount = (*ri);
  155. (*wi) = mount;
  156. if (mount->get_physical_filename() == physical_filename) {
  157. // Remove this one. Don't increment wi.
  158. delete mount;
  159. } else {
  160. // Don't remove this one.
  161. ++wi;
  162. }
  163. ++ri;
  164. }
  165. int num_removed = _mounts.end() - wi;
  166. _mounts.erase(wi, _mounts.end());
  167. return num_removed;
  168. }
  169. ////////////////////////////////////////////////////////////////////
  170. // Function: VirtualFileSystem::unmount_point
  171. // Access: Published
  172. // Description: Unmounts all systems attached to the given mount
  173. // point from the file system. Returns the number of
  174. // appearances unmounted.
  175. ////////////////////////////////////////////////////////////////////
  176. int VirtualFileSystem::
  177. unmount_point(const string &mount_point) {
  178. Filename nmp = normalize_mount_point(mount_point);
  179. Mounts::iterator ri, wi;
  180. wi = ri = _mounts.begin();
  181. while (ri != _mounts.end()) {
  182. VirtualFileMount *mount = (*ri);
  183. (*wi) = mount;
  184. if (mount->get_mount_point() == nmp) {
  185. // Remove this one. Don't increment wi.
  186. delete mount;
  187. } else {
  188. // Don't remove this one.
  189. ++wi;
  190. }
  191. ++ri;
  192. }
  193. int num_removed = _mounts.end() - wi;
  194. _mounts.erase(wi, _mounts.end());
  195. return num_removed;
  196. }
  197. ////////////////////////////////////////////////////////////////////
  198. // Function: VirtualFileSystem::unmount_all
  199. // Access: Published
  200. // Description: Unmounts all files from the file system. Returns the
  201. // number of systems unmounted.
  202. ////////////////////////////////////////////////////////////////////
  203. int VirtualFileSystem::
  204. unmount_all() {
  205. Mounts::iterator mi;
  206. for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) {
  207. VirtualFileMount *mount = (*mi);
  208. delete mount;
  209. }
  210. int num_removed = _mounts.size();
  211. _mounts.clear();
  212. return num_removed;
  213. }
  214. ////////////////////////////////////////////////////////////////////
  215. // Function: VirtualFileSystem::chdir
  216. // Access: Published
  217. // Description: Changes the current directory. This is used to
  218. // resolve relative pathnames in get_file() and/or
  219. // find_file(). Returns true if successful, false
  220. // otherwise.
  221. //
  222. // This accepts a string rather than a Filename simply
  223. // for programmer convenience from the Python prompt.
  224. ////////////////////////////////////////////////////////////////////
  225. bool VirtualFileSystem::
  226. chdir(const string &new_directory) {
  227. if (new_directory == "/") {
  228. // We can always return to the root.
  229. _cwd = new_directory;
  230. return true;
  231. }
  232. PT(VirtualFile) file = get_file(new_directory);
  233. if (file != (VirtualFile *)NULL && file->is_directory()) {
  234. _cwd = file->get_filename();
  235. return true;
  236. }
  237. return false;
  238. }
  239. ////////////////////////////////////////////////////////////////////
  240. // Function: VirtualFileSystem::get_cwd
  241. // Access: Published
  242. // Description: Returns the current directory name. See chdir().
  243. ////////////////////////////////////////////////////////////////////
  244. const Filename &VirtualFileSystem::
  245. get_cwd() const {
  246. return _cwd;
  247. }
  248. ////////////////////////////////////////////////////////////////////
  249. // Function: VirtualFileSystem::get_file
  250. // Access: Published
  251. // Description: Looks up the file by the indicated name in the file
  252. // system. Returns a VirtualFile pointer representing
  253. // the file if it is found, or NULL if it is not.
  254. ////////////////////////////////////////////////////////////////////
  255. PT(VirtualFile) VirtualFileSystem::
  256. get_file(const Filename &filename) const {
  257. nassertr(!filename.empty(), NULL);
  258. Filename pathname(filename);
  259. if (pathname.is_local()) {
  260. pathname = Filename(_cwd, filename);
  261. }
  262. pathname.standardize();
  263. string strpath = pathname.get_fullpath().substr(1);
  264. // Now scan all the mount points, from the back (since later mounts
  265. // override more recent ones), until a match is found.
  266. PT(VirtualFile) found_file = NULL;
  267. VirtualFileComposite *composite_file = NULL;
  268. Mounts::const_reverse_iterator rmi;
  269. for (rmi = _mounts.rbegin(); rmi != _mounts.rend(); ++rmi) {
  270. VirtualFileMount *mount = (*rmi);
  271. string mount_point = mount->get_mount_point();
  272. if (strpath == mount_point) {
  273. // Here's an exact match on the mount point. This filename is
  274. // the root directory of this mount object.
  275. if (found_match(found_file, composite_file, mount, "")) {
  276. return found_file;
  277. }
  278. } else if (mount_point.empty()) {
  279. // This is the root mount point; all files are in here.
  280. if (mount->has_file(strpath)) {
  281. // Bingo!
  282. if (found_match(found_file, composite_file, mount, strpath)) {
  283. return found_file;
  284. }
  285. }
  286. } else if (strpath.length() > mount_point.length() &&
  287. strpath.substr(0, mount_point.length()) == mount_point &&
  288. strpath[mount_point.length()] == '/') {
  289. // This pathname falls within this mount system.
  290. Filename local_filename = strpath.substr(mount_point.length() + 1);
  291. if (mount->has_file(local_filename)) {
  292. // Bingo!
  293. if (found_match(found_file, composite_file, mount, local_filename)) {
  294. return found_file;
  295. }
  296. }
  297. }
  298. }
  299. return found_file;
  300. }
  301. ////////////////////////////////////////////////////////////////////
  302. // Function: VirtualFileSystem::find_file
  303. // Access: Published
  304. // Description: Uses the indicated search path to find the file
  305. // within the file system. Returns the first occurrence
  306. // of the file found, or NULL if the file cannot be
  307. // found.
  308. ////////////////////////////////////////////////////////////////////
  309. PT(VirtualFile) VirtualFileSystem::
  310. find_file(const Filename &filename, const DSearchPath &searchpath) const {
  311. if (!filename.is_local()) {
  312. return get_file(filename);
  313. }
  314. int num_directories = searchpath.get_num_directories();
  315. for (int i = 0; i < num_directories; i++) {
  316. Filename match(searchpath.get_directory(i), filename);
  317. PT(VirtualFile) found_file = get_file(match);
  318. if (found_file != (VirtualFile *)NULL) {
  319. return found_file;
  320. }
  321. }
  322. return NULL;
  323. }
  324. ////////////////////////////////////////////////////////////////////
  325. // Function: VirtualFileSystem::resolve_filename
  326. // Access: Public
  327. // Description: Searches the given search path for the filename. If
  328. // it is found, updates the filename to the full
  329. // pathname found and returns true; otherwise, returns
  330. // false.
  331. ////////////////////////////////////////////////////////////////////
  332. bool VirtualFileSystem::
  333. resolve_filename(Filename &filename,
  334. const DSearchPath &searchpath,
  335. const string &default_extension) const {
  336. PT(VirtualFile) found;
  337. if (filename.is_local()) {
  338. found = find_file(filename.get_fullpath(), searchpath);
  339. if (found.is_null()) {
  340. // We didn't find it with the given extension; can we try the
  341. // default extension?
  342. if (filename.get_extension().empty() && !default_extension.empty()) {
  343. Filename try_ext = filename;
  344. try_ext.set_extension(default_extension);
  345. found = find_file(try_ext.get_fullpath(), searchpath);
  346. }
  347. }
  348. } else {
  349. if (exists(filename)) {
  350. // The full pathname exists. Return true.
  351. return true;
  352. } else {
  353. // The full pathname doesn't exist with the given extension;
  354. // does it exist with the default extension?
  355. if (filename.get_extension().empty() && !default_extension.empty()) {
  356. Filename try_ext = filename;
  357. try_ext.set_extension(default_extension);
  358. found = get_file(try_ext);
  359. }
  360. }
  361. }
  362. if (!found.is_null()) {
  363. filename = found->get_filename();
  364. return true;
  365. }
  366. return false;
  367. }
  368. ////////////////////////////////////////////////////////////////////
  369. // Function: VirtualFileSystem::write
  370. // Access: Published
  371. // Description:
  372. ////////////////////////////////////////////////////////////////////
  373. void VirtualFileSystem::
  374. write(ostream &out) const {
  375. Mounts::const_iterator mi;
  376. for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) {
  377. VirtualFileMount *mount = (*mi);
  378. mount->write(out);
  379. }
  380. }
  381. ////////////////////////////////////////////////////////////////////
  382. // Function: VirtualFileSystem::get_global_ptr
  383. // Access: Published, Static
  384. // Description: Returns the default global VirtualFileSystem. You
  385. // may create your own personal VirtualFileSystem
  386. // objects and use them for whatever you like, but Panda
  387. // will attempt to load models and stuff from this
  388. // default object.
  389. //
  390. // Initially, the global VirtualFileSystem is set up to
  391. // mount the OS filesystem to root; i.e. it is
  392. // equivalent to the OS filesystem. This may be
  393. // subsequently adjusted by the user.
  394. ////////////////////////////////////////////////////////////////////
  395. VirtualFileSystem *VirtualFileSystem::
  396. get_global_ptr() {
  397. if (_global_ptr == (VirtualFileSystem *)NULL) {
  398. _global_ptr = new VirtualFileSystem;
  399. // Set up the default mounts. First, there is always the root
  400. // mount.
  401. _global_ptr->mount("/", "/", 0);
  402. // Then, we add whatever mounts are listed in the Configrc file.
  403. Config::ConfigTable::Symbol mounts;
  404. config_util.GetAll("vfs-mount", mounts);
  405. Config::ConfigTable::Symbol::iterator si;
  406. for (si = mounts.begin(); si != mounts.end(); ++si) {
  407. string mount_desc = (*si).Val();
  408. // The last space marks the beginning of the mount point.
  409. // Spaces before that are part of the system filename.
  410. size_t space = mount_desc.rfind(' ');
  411. if (space == string::npos) {
  412. util_cat.warning()
  413. << "No space in vfs-mount descriptor: " << mount_desc << "\n";
  414. } else {
  415. string fn = trim_right(mount_desc.substr(0, space));
  416. fn = ExecutionEnvironment::expand_string(fn);
  417. Filename physical_filename = Filename::from_os_specific(fn);
  418. string mount_point = mount_desc.substr(space + 1);
  419. _global_ptr->mount(physical_filename, mount_point, 0);
  420. }
  421. }
  422. }
  423. return _global_ptr;
  424. }
  425. ////////////////////////////////////////////////////////////////////
  426. // Function: VirtualFileSystem::scan_mount_points
  427. // Access: Public
  428. // Description: Adds to names a list of all the mount points in use
  429. // that are one directory below path, if any. That is,
  430. // these are the external files or directories mounted
  431. // directly to the indicated path.
  432. //
  433. // The names vector is filled with a set of basenames,
  434. // the basename part of the mount point.
  435. ////////////////////////////////////////////////////////////////////
  436. void VirtualFileSystem::
  437. scan_mount_points(vector_string &names, const Filename &path) const {
  438. nassertv(!path.empty() && !path.is_local());
  439. string prefix = path.get_fullpath().substr(1);
  440. Mounts::const_iterator mi;
  441. for (mi = _mounts.begin(); mi != _mounts.end(); ++mi) {
  442. VirtualFileMount *mount = (*mi);
  443. string mount_point = mount->get_mount_point();
  444. if (prefix.empty()) {
  445. // The indicated path is the root. Is the mount point on the
  446. // root?
  447. if (mount_point.find('/') == string::npos) {
  448. // No embedded slashes, so the mount point is only one
  449. // directory below the root.
  450. names.push_back(mount_point);
  451. }
  452. } else {
  453. if (mount_point.substr(0, prefix.length()) == prefix &&
  454. mount_point.length() > prefix.length() &&
  455. mount_point[prefix.length()] == '/') {
  456. // This mount point is below the indicated path. Is it only one
  457. // directory below?
  458. string basename = mount_point.substr(prefix.length());
  459. if (basename.find('/') == string::npos) {
  460. // No embedded slashes, so it's only one directory below.
  461. names.push_back(basename);
  462. }
  463. }
  464. }
  465. }
  466. }
  467. ////////////////////////////////////////////////////////////////////
  468. // Function: VirtualFileSystem::normalize_mount_point
  469. // Access: Private
  470. // Description: Converts the mount point string supplied by the user
  471. // to standard form (relative to the current directory,
  472. // with no double slashes, and not terminating with a
  473. // slash). The initial slash is removed.
  474. ////////////////////////////////////////////////////////////////////
  475. Filename VirtualFileSystem::
  476. normalize_mount_point(const string &mount_point) const {
  477. Filename nmp = mount_point;
  478. if (nmp.is_local()) {
  479. nmp = Filename(_cwd, mount_point);
  480. }
  481. nmp.standardize();
  482. nassertr(!nmp.empty() && nmp[0] == '/', nmp);
  483. return nmp.get_fullpath().substr(1);
  484. }
  485. ////////////////////////////////////////////////////////////////////
  486. // Function: VirtualFileSystem::found_match
  487. // Access: Private
  488. // Description: Evaluates one match found during a get_file()
  489. // operation. There may be multiple matches for a
  490. // particular filename due to the ambiguities introduced
  491. // by allowing multiple mount points, so we may have to
  492. // keep searching even after the first match is found.
  493. //
  494. // Returns true if the search should terminate now, or
  495. // false if it should keep iterating.
  496. ////////////////////////////////////////////////////////////////////
  497. bool VirtualFileSystem::
  498. found_match(PT(VirtualFile) &found_file, VirtualFileComposite *&composite_file,
  499. VirtualFileMount *mount, const string &local_filename) const {
  500. if (found_file == (VirtualFile *)NULL) {
  501. // This was our first match. Save it.
  502. found_file = new VirtualFileSimple(mount, local_filename);
  503. if (!mount->is_directory(local_filename)) {
  504. // If it's not a directory, we're done.
  505. return true;
  506. }
  507. } else {
  508. // This was our second match. The previous match(es) must
  509. // have been directories.
  510. if (!mount->is_directory(local_filename)) {
  511. // However, this one isn't a directory. We're done.
  512. return true;
  513. }
  514. // At least two directories matched to the same path. We
  515. // need a composite directory.
  516. if (composite_file == (VirtualFileComposite *)NULL) {
  517. composite_file =
  518. new VirtualFileComposite((VirtualFileSystem *)this, found_file->get_filename());
  519. composite_file->add_component(found_file);
  520. found_file = composite_file;
  521. }
  522. composite_file->add_component(new VirtualFileSimple(mount, local_filename));
  523. }
  524. // Keep going, looking for more directories.
  525. return false;
  526. }