PageRenderTime 206ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/src/vfs/extfs/extfs.c

https://gitlab.com/oyvholm/mc
C | 1757 lines | 1348 code | 268 blank | 141 comment | 292 complexity | 3c8b20388e43e7e0518f039b4fc3d4ad MD5 | raw file
  1. /*
  2. Virtual File System: External file system.
  3. Copyright (C) 1995-2016
  4. Free Software Foundation, Inc.
  5. Written by:
  6. Jakub Jelinek, 1995
  7. Pavel Machek, 1998
  8. Andrew T. Veliath, 1999
  9. Slava Zanko <slavazanko@gmail.com>, 2013
  10. This file is part of the Midnight Commander.
  11. The Midnight Commander is free software: you can redistribute it
  12. and/or modify it under the terms of the GNU General Public License as
  13. published by the Free Software Foundation, either version 3 of the License,
  14. or (at your option) any later version.
  15. The Midnight Commander is distributed in the hope that it will be useful,
  16. but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. GNU General Public License for more details.
  19. You should have received a copy of the GNU General Public License
  20. along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. /**
  23. * \file
  24. * \brief Source: Virtual File System: External file system
  25. * \author Jakub Jelinek
  26. * \author Pavel Machek
  27. * \author Andrew T. Veliath
  28. * \date 1995, 1998, 1999
  29. */
  30. /* Namespace: init_extfs */
  31. #include <config.h>
  32. #include <stdio.h>
  33. #include <ctype.h>
  34. #include <string.h>
  35. #include <stdlib.h>
  36. #include <sys/types.h>
  37. #include <sys/stat.h>
  38. #include <unistd.h>
  39. #include <signal.h>
  40. #include <errno.h>
  41. #include <sys/wait.h>
  42. #include "lib/global.h"
  43. #include "lib/fileloc.h"
  44. #include "lib/mcconfig.h"
  45. #include "lib/util.h"
  46. #include "lib/widget.h" /* message() */
  47. #include "src/execute.h" /* For shell_execute */
  48. #include "lib/vfs/vfs.h"
  49. #include "lib/vfs/utilvfs.h"
  50. #include "lib/vfs/gc.h" /* vfs_rmstamp */
  51. #include "extfs.h"
  52. /*** global variables ****************************************************************************/
  53. /*** file scope macro definitions ****************************************************************/
  54. #undef ERRNOR
  55. #define ERRNOR(x,y) do { my_errno = x; return y; } while(0)
  56. #define RECORDSIZE 512
  57. /*** file scope type declarations ****************************************************************/
  58. struct inode
  59. {
  60. nlink_t nlink;
  61. struct entry *first_in_subdir; /* only used if this is a directory */
  62. struct entry *last_in_subdir;
  63. ino_t inode; /* This is inode # */
  64. dev_t dev; /* This is an internal identification of the extfs archive */
  65. struct archive *archive; /* And this is an archive structure */
  66. dev_t rdev;
  67. mode_t mode;
  68. uid_t uid;
  69. gid_t gid;
  70. off_t size;
  71. time_t mtime;
  72. char *linkname;
  73. time_t atime;
  74. time_t ctime;
  75. char *local_filename;
  76. };
  77. struct entry
  78. {
  79. struct entry *next_in_dir;
  80. struct entry *dir;
  81. char *name;
  82. struct inode *inode;
  83. };
  84. struct pseudofile
  85. {
  86. struct archive *archive;
  87. gboolean has_changed;
  88. int local_handle;
  89. struct entry *entry;
  90. };
  91. struct archive
  92. {
  93. int fstype;
  94. char *name;
  95. char *local_name;
  96. struct stat local_stat;
  97. dev_t rdev;
  98. int fd_usage;
  99. ino_t inode_counter;
  100. struct entry *root_entry;
  101. struct archive *next;
  102. };
  103. typedef struct
  104. {
  105. char *path;
  106. char *prefix;
  107. gboolean need_archive;
  108. } extfs_plugin_info_t;
  109. /*** file scope variables ************************************************************************/
  110. static GArray *extfs_plugins = NULL;
  111. static gboolean errloop;
  112. static gboolean notadir;
  113. static struct vfs_class vfs_extfs_ops;
  114. static struct archive *first_archive = NULL;
  115. static int my_errno = 0;
  116. /*** file scope functions ************************************************************************/
  117. /* --------------------------------------------------------------------------------------------- */
  118. static void extfs_remove_entry (struct entry *e);
  119. static void extfs_free (vfsid id);
  120. static void extfs_free_entry (struct entry *e);
  121. static struct entry *extfs_resolve_symlinks_int (struct entry *entry, GSList * list);
  122. /* --------------------------------------------------------------------------------------------- */
  123. static void
  124. extfs_make_dots (struct entry *ent)
  125. {
  126. struct entry *entry = g_new (struct entry, 1);
  127. struct entry *parentry = ent->dir;
  128. struct inode *inode = ent->inode, *parent;
  129. parent = (parentry != NULL) ? parentry->inode : NULL;
  130. entry->name = g_strdup (".");
  131. entry->inode = inode;
  132. entry->dir = ent;
  133. inode->local_filename = NULL;
  134. inode->first_in_subdir = entry;
  135. inode->nlink++;
  136. entry->next_in_dir = g_new (struct entry, 1);
  137. entry = entry->next_in_dir;
  138. entry->name = g_strdup ("..");
  139. inode->last_in_subdir = entry;
  140. entry->next_in_dir = NULL;
  141. if (parent != NULL)
  142. {
  143. entry->inode = parent;
  144. entry->dir = parentry;
  145. parent->nlink++;
  146. }
  147. else
  148. {
  149. entry->inode = inode;
  150. entry->dir = ent;
  151. inode->nlink++;
  152. }
  153. }
  154. /* --------------------------------------------------------------------------------------------- */
  155. static struct entry *
  156. extfs_generate_entry (struct archive *archive,
  157. const char *name, struct entry *parentry, mode_t mode)
  158. {
  159. mode_t myumask;
  160. struct inode *inode, *parent;
  161. struct entry *entry;
  162. parent = (parentry != NULL) ? parentry->inode : NULL;
  163. entry = g_new (struct entry, 1);
  164. entry->name = g_strdup (name);
  165. entry->next_in_dir = NULL;
  166. entry->dir = parentry;
  167. if (parent != NULL)
  168. {
  169. parent->last_in_subdir->next_in_dir = entry;
  170. parent->last_in_subdir = entry;
  171. }
  172. inode = g_new (struct inode, 1);
  173. entry->inode = inode;
  174. inode->local_filename = NULL;
  175. inode->linkname = NULL;
  176. inode->last_in_subdir = NULL;
  177. inode->inode = (archive->inode_counter)++;
  178. inode->dev = archive->rdev;
  179. inode->archive = archive;
  180. myumask = umask (022);
  181. umask (myumask);
  182. inode->mode = mode & ~myumask;
  183. mode = inode->mode;
  184. inode->rdev = 0;
  185. inode->uid = getuid ();
  186. inode->gid = getgid ();
  187. inode->size = 0;
  188. inode->mtime = time (NULL);
  189. inode->atime = inode->mtime;
  190. inode->ctime = inode->mtime;
  191. inode->nlink = 1;
  192. if (S_ISDIR (mode))
  193. extfs_make_dots (entry);
  194. return entry;
  195. }
  196. /* --------------------------------------------------------------------------------------------- */
  197. static struct entry *
  198. extfs_find_entry_int (struct entry *dir, const char *name, GSList * list,
  199. gboolean make_dirs, gboolean make_file)
  200. {
  201. struct entry *pent, *pdir;
  202. const char *p, *name_end;
  203. char *q;
  204. char c = PATH_SEP;
  205. if (g_path_is_absolute (name))
  206. {
  207. /* Handle absolute paths */
  208. name = g_path_skip_root (name);
  209. dir = dir->inode->archive->root_entry;
  210. }
  211. pent = dir;
  212. p = name;
  213. name_end = name + strlen (name);
  214. q = strchr (p, PATH_SEP);
  215. if (q == NULL)
  216. q = strchr (p, '\0');
  217. while ((pent != NULL) && (c != '\0') && (*p != '\0'))
  218. {
  219. c = *q;
  220. *q = '\0';
  221. if (!DIR_IS_DOT (p))
  222. {
  223. if (DIR_IS_DOTDOT (p))
  224. pent = pent->dir;
  225. else
  226. {
  227. pent = extfs_resolve_symlinks_int (pent, list);
  228. if (pent == NULL)
  229. {
  230. *q = c;
  231. return NULL;
  232. }
  233. if (!S_ISDIR (pent->inode->mode))
  234. {
  235. *q = c;
  236. notadir = TRUE;
  237. return NULL;
  238. }
  239. pdir = pent;
  240. for (pent = pent->inode->first_in_subdir; pent != NULL; pent = pent->next_in_dir)
  241. /* Hack: I keep the original semanthic unless
  242. q+1 would break in the strchr */
  243. if (strcmp (pent->name, p) == 0)
  244. {
  245. if (q + 1 > name_end)
  246. {
  247. *q = c;
  248. notadir = !S_ISDIR (pent->inode->mode);
  249. return pent;
  250. }
  251. break;
  252. }
  253. /* When we load archive, we create automagically
  254. * non-existent directories
  255. */
  256. if (pent == NULL && make_dirs)
  257. pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFDIR | 0777);
  258. if (pent == NULL && make_file)
  259. pent = extfs_generate_entry (dir->inode->archive, p, pdir, S_IFREG | 0666);
  260. }
  261. }
  262. /* Next iteration */
  263. *q = c;
  264. if (c == '\0')
  265. break;
  266. p = q + 1;
  267. q = strchr (p, PATH_SEP);
  268. if (q == NULL)
  269. q = strchr (p, '\0');
  270. }
  271. if (pent == NULL)
  272. my_errno = ENOENT;
  273. return pent;
  274. }
  275. /* --------------------------------------------------------------------------------------------- */
  276. static struct entry *
  277. extfs_find_entry (struct entry *dir, const char *name, gboolean make_dirs, gboolean make_file)
  278. {
  279. struct entry *res;
  280. errloop = FALSE;
  281. notadir = FALSE;
  282. res = extfs_find_entry_int (dir, name, NULL, make_dirs, make_file);
  283. if (res == NULL)
  284. {
  285. if (errloop)
  286. my_errno = ELOOP;
  287. else if (notadir)
  288. my_errno = ENOTDIR;
  289. }
  290. return res;
  291. }
  292. /* --------------------------------------------------------------------------------------------- */
  293. static void
  294. extfs_fill_names (struct vfs_class *me, fill_names_f func)
  295. {
  296. struct archive *a = first_archive;
  297. (void) me;
  298. while (a != NULL)
  299. {
  300. extfs_plugin_info_t *info;
  301. char *name;
  302. info = &g_array_index (extfs_plugins, extfs_plugin_info_t, a->fstype);
  303. name =
  304. g_strconcat (a->name ? a->name : "", PATH_SEP_STR, info->prefix, VFS_PATH_URL_DELIMITER,
  305. (char *) NULL);
  306. func (name);
  307. g_free (name);
  308. a = a->next;
  309. }
  310. }
  311. /* --------------------------------------------------------------------------------------------- */
  312. static void
  313. extfs_free_archive (struct archive *archive)
  314. {
  315. extfs_free_entry (archive->root_entry);
  316. if (archive->local_name != NULL)
  317. {
  318. struct stat my;
  319. vfs_path_t *local_name_vpath, *name_vpath;
  320. local_name_vpath = vfs_path_from_str (archive->local_name);
  321. name_vpath = vfs_path_from_str (archive->name);
  322. mc_stat (local_name_vpath, &my);
  323. mc_ungetlocalcopy (name_vpath, local_name_vpath,
  324. archive->local_stat.st_mtime != my.st_mtime);
  325. vfs_path_free (local_name_vpath);
  326. vfs_path_free (name_vpath);
  327. g_free (archive->local_name);
  328. }
  329. g_free (archive->name);
  330. g_free (archive);
  331. }
  332. /* --------------------------------------------------------------------------------------------- */
  333. static FILE *
  334. extfs_open_archive (int fstype, const char *name, struct archive **pparc)
  335. {
  336. const extfs_plugin_info_t *info;
  337. static dev_t archive_counter = 0;
  338. FILE *result = NULL;
  339. mode_t mode;
  340. char *cmd;
  341. struct stat mystat;
  342. struct archive *current_archive;
  343. struct entry *root_entry;
  344. char *tmp = NULL;
  345. vfs_path_t *local_name_vpath = NULL;
  346. vfs_path_t *name_vpath;
  347. name_vpath = vfs_path_from_str (name);
  348. info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
  349. if (info->need_archive)
  350. {
  351. if (mc_stat (name_vpath, &mystat) == -1)
  352. goto ret;
  353. if (!vfs_file_is_local (name_vpath))
  354. {
  355. local_name_vpath = mc_getlocalcopy (name_vpath);
  356. if (local_name_vpath == NULL)
  357. goto ret;
  358. }
  359. tmp = name_quote (vfs_path_get_last_path_str (name_vpath), FALSE);
  360. }
  361. cmd = g_strconcat (info->path, info->prefix, " list ",
  362. vfs_path_get_last_path_str (local_name_vpath) != NULL ?
  363. vfs_path_get_last_path_str (local_name_vpath) : tmp, (char *) NULL);
  364. g_free (tmp);
  365. open_error_pipe ();
  366. result = popen (cmd, "r");
  367. g_free (cmd);
  368. if (result == NULL)
  369. {
  370. close_error_pipe (D_ERROR, NULL);
  371. if (local_name_vpath != NULL)
  372. {
  373. mc_ungetlocalcopy (name_vpath, local_name_vpath, FALSE);
  374. vfs_path_free (local_name_vpath);
  375. }
  376. goto ret;
  377. }
  378. #ifdef ___QNXNTO__
  379. setvbuf (result, NULL, _IONBF, 0);
  380. #endif
  381. current_archive = g_new (struct archive, 1);
  382. current_archive->fstype = fstype;
  383. current_archive->name = g_strdup (name);
  384. current_archive->local_name = g_strdup (vfs_path_get_last_path_str (local_name_vpath));
  385. if (local_name_vpath != NULL)
  386. {
  387. mc_stat (local_name_vpath, &current_archive->local_stat);
  388. vfs_path_free (local_name_vpath);
  389. }
  390. current_archive->inode_counter = 0;
  391. current_archive->fd_usage = 0;
  392. current_archive->rdev = archive_counter++;
  393. current_archive->next = first_archive;
  394. first_archive = current_archive;
  395. mode = mystat.st_mode & 07777;
  396. if (mode & 0400)
  397. mode |= 0100;
  398. if (mode & 0040)
  399. mode |= 0010;
  400. if (mode & 0004)
  401. mode |= 0001;
  402. mode |= S_IFDIR;
  403. root_entry = extfs_generate_entry (current_archive, PATH_SEP_STR, NULL, mode);
  404. root_entry->inode->uid = mystat.st_uid;
  405. root_entry->inode->gid = mystat.st_gid;
  406. root_entry->inode->atime = mystat.st_atime;
  407. root_entry->inode->ctime = mystat.st_ctime;
  408. root_entry->inode->mtime = mystat.st_mtime;
  409. current_archive->root_entry = root_entry;
  410. *pparc = current_archive;
  411. ret:
  412. vfs_path_free (name_vpath);
  413. return result;
  414. }
  415. /* --------------------------------------------------------------------------------------------- */
  416. /**
  417. * Main loop for reading an archive.
  418. * Return 0 on success, -1 on error.
  419. */
  420. static int
  421. extfs_read_archive (int fstype, const char *name, struct archive **pparc)
  422. {
  423. FILE *extfsd;
  424. const extfs_plugin_info_t *info;
  425. char *buffer;
  426. struct archive *current_archive;
  427. char *current_file_name, *current_link_name;
  428. info = &g_array_index (extfs_plugins, extfs_plugin_info_t, fstype);
  429. extfsd = extfs_open_archive (fstype, name, &current_archive);
  430. if (extfsd == NULL)
  431. {
  432. message (D_ERROR, MSG_ERROR, _("Cannot open %s archive\n%s"), info->prefix, name);
  433. return -1;
  434. }
  435. buffer = g_malloc (BUF_4K);
  436. while (fgets (buffer, BUF_4K, extfsd) != NULL)
  437. {
  438. struct stat hstat;
  439. current_link_name = NULL;
  440. if (vfs_parse_ls_lga (buffer, &hstat, &current_file_name, &current_link_name, NULL))
  441. {
  442. struct entry *entry, *pent;
  443. struct inode *inode;
  444. char *p, *q, *cfn = current_file_name;
  445. if (*cfn != '\0')
  446. {
  447. if (IS_PATH_SEP (*cfn))
  448. cfn++;
  449. p = strchr (cfn, '\0');
  450. if (p != cfn && IS_PATH_SEP (p[-1]))
  451. p[-1] = '\0';
  452. p = strrchr (cfn, PATH_SEP);
  453. if (p == NULL)
  454. {
  455. p = cfn;
  456. q = strchr (cfn, '\0');
  457. }
  458. else
  459. {
  460. *(p++) = '\0';
  461. q = cfn;
  462. }
  463. if (S_ISDIR (hstat.st_mode) && (DIR_IS_DOT (p) || DIR_IS_DOTDOT (p)))
  464. goto read_extfs_continue;
  465. pent = extfs_find_entry (current_archive->root_entry, q, TRUE, FALSE);
  466. if (pent == NULL)
  467. {
  468. /* FIXME: Should clean everything one day */
  469. g_free (buffer);
  470. pclose (extfsd);
  471. close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
  472. return -1;
  473. }
  474. entry = g_new (struct entry, 1);
  475. entry->name = g_strdup (p);
  476. entry->next_in_dir = NULL;
  477. entry->dir = pent;
  478. if (pent->inode->last_in_subdir)
  479. {
  480. pent->inode->last_in_subdir->next_in_dir = entry;
  481. pent->inode->last_in_subdir = entry;
  482. }
  483. if (!S_ISLNK (hstat.st_mode) && (current_link_name != NULL))
  484. {
  485. pent = extfs_find_entry (current_archive->root_entry,
  486. current_link_name, FALSE, FALSE);
  487. if (pent == NULL)
  488. {
  489. /* FIXME: Should clean everything one day */
  490. g_free (buffer);
  491. pclose (extfsd);
  492. close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
  493. return -1;
  494. }
  495. entry->inode = pent->inode;
  496. pent->inode->nlink++;
  497. }
  498. else
  499. {
  500. inode = g_new (struct inode, 1);
  501. entry->inode = inode;
  502. inode->local_filename = NULL;
  503. inode->inode = (current_archive->inode_counter)++;
  504. inode->nlink = 1;
  505. inode->dev = current_archive->rdev;
  506. inode->archive = current_archive;
  507. inode->mode = hstat.st_mode;
  508. #ifdef HAVE_STRUCT_STAT_ST_RDEV
  509. inode->rdev = hstat.st_rdev;
  510. #else
  511. inode->rdev = 0;
  512. #endif
  513. inode->uid = hstat.st_uid;
  514. inode->gid = hstat.st_gid;
  515. inode->size = hstat.st_size;
  516. inode->mtime = hstat.st_mtime;
  517. inode->atime = hstat.st_atime;
  518. inode->ctime = hstat.st_ctime;
  519. inode->first_in_subdir = NULL;
  520. inode->last_in_subdir = NULL;
  521. if (current_link_name != NULL && S_ISLNK (hstat.st_mode))
  522. {
  523. inode->linkname = current_link_name;
  524. current_link_name = NULL;
  525. }
  526. else
  527. {
  528. if (S_ISLNK (hstat.st_mode))
  529. inode->mode &= ~S_IFLNK; /* You *DON'T* want to do this always */
  530. inode->linkname = NULL;
  531. }
  532. if (S_ISDIR (hstat.st_mode))
  533. extfs_make_dots (entry);
  534. }
  535. }
  536. read_extfs_continue:
  537. g_free (current_file_name);
  538. g_free (current_link_name);
  539. }
  540. }
  541. g_free (buffer);
  542. /* Check if extfs 'list' returned 0 */
  543. if (pclose (extfsd) != 0)
  544. {
  545. extfs_free (current_archive);
  546. close_error_pipe (D_ERROR, _("Inconsistent extfs archive"));
  547. return -1;
  548. }
  549. close_error_pipe (D_ERROR, NULL);
  550. *pparc = current_archive;
  551. return 0;
  552. }
  553. /* --------------------------------------------------------------------------------------------- */
  554. static int
  555. extfs_which (struct vfs_class *me, const char *path)
  556. {
  557. size_t path_len;
  558. size_t i;
  559. (void) me;
  560. path_len = strlen (path);
  561. for (i = 0; i < extfs_plugins->len; i++)
  562. {
  563. extfs_plugin_info_t *info;
  564. info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
  565. if ((strncmp (path, info->prefix, path_len) == 0)
  566. && ((info->prefix[path_len] == '\0') || (info->prefix[path_len] == '+')))
  567. return i;
  568. }
  569. return -1;
  570. }
  571. /* --------------------------------------------------------------------------------------------- */
  572. /**
  573. * Dissect the path and create corresponding superblock.
  574. */
  575. static const char *
  576. extfs_get_path_int (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
  577. {
  578. char *archive_name;
  579. int result = -1;
  580. struct archive *parc;
  581. int fstype;
  582. const vfs_path_element_t *path_element;
  583. path_element = vfs_path_get_by_index (vpath, -1);
  584. fstype = extfs_which (path_element->class, path_element->vfs_prefix);
  585. if (fstype == -1)
  586. return NULL;
  587. archive_name = vfs_path_to_str_elements_count (vpath, -1);
  588. /*
  589. * All filesystems should have some local archive, at least
  590. * it can be PATH_SEP ('/').
  591. */
  592. for (parc = first_archive; parc != NULL; parc = parc->next)
  593. if (parc->name != NULL)
  594. {
  595. if (strcmp (parc->name, archive_name) == 0)
  596. {
  597. vfs_stamp (&vfs_extfs_ops, (vfsid) parc);
  598. g_free (archive_name);
  599. goto return_success;
  600. }
  601. }
  602. result = do_not_open ? -1 : extfs_read_archive (fstype, archive_name, &parc);
  603. g_free (archive_name);
  604. if (result == -1)
  605. {
  606. path_element->class->verrno = EIO;
  607. return NULL;
  608. }
  609. return_success:
  610. *archive = parc;
  611. return path_element->path;
  612. }
  613. /* --------------------------------------------------------------------------------------------- */
  614. /**
  615. * Dissect the path and create corresponding superblock.
  616. * The result should be freed.
  617. */
  618. static char *
  619. extfs_get_path (const vfs_path_t * vpath, struct archive **archive, gboolean do_not_open)
  620. {
  621. return g_strdup (extfs_get_path_int (vpath, archive, do_not_open));
  622. }
  623. /* --------------------------------------------------------------------------------------------- */
  624. /* Return allocated path (without leading slash) inside the archive */
  625. static char *
  626. extfs_get_path_from_entry (struct entry *entry)
  627. {
  628. GString *localpath;
  629. localpath = g_string_new ("");
  630. while (entry->dir != NULL)
  631. {
  632. g_string_prepend (localpath, entry->name);
  633. if (entry->dir->dir != NULL)
  634. g_string_prepend_c (localpath, PATH_SEP);
  635. entry = entry->dir;
  636. }
  637. return g_string_free (localpath, FALSE);
  638. }
  639. /* --------------------------------------------------------------------------------------------- */
  640. static struct entry *
  641. extfs_resolve_symlinks_int (struct entry *entry, GSList * list)
  642. {
  643. struct entry *pent = NULL;
  644. if (!S_ISLNK (entry->inode->mode))
  645. return entry;
  646. if (g_slist_find (list, entry) != NULL)
  647. {
  648. /* Here we protect us against symlink looping */
  649. errloop = TRUE;
  650. }
  651. else
  652. {
  653. GSList *looping;
  654. looping = g_slist_prepend (list, entry);
  655. pent = extfs_find_entry_int (entry->dir, entry->inode->linkname, looping, FALSE, FALSE);
  656. looping = g_slist_delete_link (looping, looping);
  657. if (pent == NULL)
  658. my_errno = ENOENT;
  659. }
  660. return pent;
  661. }
  662. /* --------------------------------------------------------------------------------------------- */
  663. static struct entry *
  664. extfs_resolve_symlinks (struct entry *entry)
  665. {
  666. struct entry *res;
  667. errloop = FALSE;
  668. notadir = FALSE;
  669. res = extfs_resolve_symlinks_int (entry, NULL);
  670. if (res == NULL)
  671. {
  672. if (errloop)
  673. my_errno = ELOOP;
  674. else if (notadir)
  675. my_errno = ENOTDIR;
  676. }
  677. return res;
  678. }
  679. /* --------------------------------------------------------------------------------------------- */
  680. static char *
  681. extfs_get_archive_name (struct archive *archive)
  682. {
  683. const char *archive_name;
  684. if (archive->local_name)
  685. archive_name = archive->local_name;
  686. else
  687. archive_name = archive->name;
  688. if (!archive_name || !*archive_name)
  689. return g_strdup ("no_archive_name");
  690. else
  691. {
  692. char *ret_str;
  693. vfs_path_t *vpath;
  694. const vfs_path_element_t *path_element;
  695. vpath = vfs_path_from_str (archive_name);
  696. path_element = vfs_path_get_by_index (vpath, -1);
  697. ret_str = g_strdup (path_element->path);
  698. vfs_path_free (vpath);
  699. return ret_str;
  700. }
  701. }
  702. /* --------------------------------------------------------------------------------------------- */
  703. /** Don't pass localname as NULL */
  704. static int
  705. extfs_cmd (const char *str_extfs_cmd, struct archive *archive,
  706. struct entry *entry, const char *localname)
  707. {
  708. char *file;
  709. char *quoted_file;
  710. char *quoted_localname;
  711. char *archive_name, *quoted_archive_name;
  712. const extfs_plugin_info_t *info;
  713. char *cmd;
  714. int retval;
  715. file = extfs_get_path_from_entry (entry);
  716. quoted_file = name_quote (file, FALSE);
  717. g_free (file);
  718. archive_name = extfs_get_archive_name (archive);
  719. quoted_archive_name = name_quote (archive_name, FALSE);
  720. g_free (archive_name);
  721. quoted_localname = name_quote (localname, FALSE);
  722. info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
  723. cmd = g_strconcat (info->path, info->prefix, str_extfs_cmd,
  724. quoted_archive_name, " ", quoted_file, " ", quoted_localname, (char *) NULL);
  725. g_free (quoted_file);
  726. g_free (quoted_localname);
  727. g_free (quoted_archive_name);
  728. open_error_pipe ();
  729. retval = my_system (EXECUTE_AS_SHELL, mc_global.shell->path, cmd);
  730. g_free (cmd);
  731. close_error_pipe (D_ERROR, NULL);
  732. return retval;
  733. }
  734. /* --------------------------------------------------------------------------------------------- */
  735. static void
  736. extfs_run (const vfs_path_t * vpath)
  737. {
  738. struct archive *archive = NULL;
  739. char *p, *q, *archive_name, *quoted_archive_name;
  740. char *cmd;
  741. const extfs_plugin_info_t *info;
  742. p = extfs_get_path (vpath, &archive, FALSE);
  743. if (p == NULL)
  744. return;
  745. q = name_quote (p, FALSE);
  746. g_free (p);
  747. archive_name = extfs_get_archive_name (archive);
  748. quoted_archive_name = name_quote (archive_name, FALSE);
  749. g_free (archive_name);
  750. info = &g_array_index (extfs_plugins, extfs_plugin_info_t, archive->fstype);
  751. cmd =
  752. g_strconcat (info->path, info->prefix, " run ", quoted_archive_name, " ", q, (char *) NULL);
  753. g_free (quoted_archive_name);
  754. g_free (q);
  755. shell_execute (cmd, 0);
  756. g_free (cmd);
  757. }
  758. /* --------------------------------------------------------------------------------------------- */
  759. static void *
  760. extfs_open (const vfs_path_t * vpath, int flags, mode_t mode)
  761. {
  762. struct pseudofile *extfs_info;
  763. struct archive *archive = NULL;
  764. char *q;
  765. struct entry *entry;
  766. int local_handle;
  767. gboolean created = FALSE;
  768. q = extfs_get_path (vpath, &archive, FALSE);
  769. if (q == NULL)
  770. return NULL;
  771. entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
  772. if ((entry == NULL) && ((flags & O_CREAT) != 0))
  773. {
  774. /* Create new entry */
  775. entry = extfs_find_entry (archive->root_entry, q, FALSE, TRUE);
  776. created = (entry != NULL);
  777. }
  778. g_free (q);
  779. if (entry == NULL)
  780. return NULL;
  781. entry = extfs_resolve_symlinks (entry);
  782. if (entry == NULL)
  783. return NULL;
  784. if (S_ISDIR (entry->inode->mode))
  785. ERRNOR (EISDIR, NULL);
  786. if (entry->inode->local_filename == NULL)
  787. {
  788. vfs_path_t *local_filename_vpath;
  789. const char *local_filename;
  790. local_handle = vfs_mkstemps (&local_filename_vpath, "extfs", entry->name);
  791. if (local_handle == -1)
  792. return NULL;
  793. close (local_handle);
  794. local_filename = vfs_path_get_by_index (local_filename_vpath, -1)->path;
  795. if (!created && ((flags & O_TRUNC) == 0)
  796. && extfs_cmd (" copyout ", archive, entry, local_filename))
  797. {
  798. unlink (local_filename);
  799. vfs_path_free (local_filename_vpath);
  800. my_errno = EIO;
  801. return NULL;
  802. }
  803. entry->inode->local_filename = g_strdup (local_filename);
  804. vfs_path_free (local_filename_vpath);
  805. }
  806. local_handle = open (entry->inode->local_filename, NO_LINEAR (flags), mode);
  807. if (local_handle == -1)
  808. {
  809. /* file exists(may be). Need to drop O_CREAT flag and truncate file content */
  810. flags = ~O_CREAT & (NO_LINEAR (flags) | O_TRUNC);
  811. local_handle = open (entry->inode->local_filename, flags, mode);
  812. }
  813. if (local_handle == -1)
  814. ERRNOR (EIO, NULL);
  815. extfs_info = g_new (struct pseudofile, 1);
  816. extfs_info->archive = archive;
  817. extfs_info->entry = entry;
  818. extfs_info->has_changed = created;
  819. extfs_info->local_handle = local_handle;
  820. /* i.e. we had no open files and now we have one */
  821. vfs_rmstamp (&vfs_extfs_ops, (vfsid) archive);
  822. archive->fd_usage++;
  823. return extfs_info;
  824. }
  825. /* --------------------------------------------------------------------------------------------- */
  826. static ssize_t
  827. extfs_read (void *data, char *buffer, size_t count)
  828. {
  829. struct pseudofile *file = (struct pseudofile *) data;
  830. return read (file->local_handle, buffer, count);
  831. }
  832. /* --------------------------------------------------------------------------------------------- */
  833. static int
  834. extfs_close (void *data)
  835. {
  836. struct pseudofile *file;
  837. int errno_code = 0;
  838. file = (struct pseudofile *) data;
  839. close (file->local_handle);
  840. /* Commit the file if it has changed */
  841. if (file->has_changed)
  842. {
  843. struct stat file_status;
  844. if (extfs_cmd (" copyin ", file->archive, file->entry, file->entry->inode->local_filename))
  845. errno_code = EIO;
  846. if (stat (file->entry->inode->local_filename, &file_status) != 0)
  847. errno_code = EIO;
  848. else
  849. file->entry->inode->size = file_status.st_size;
  850. file->entry->inode->mtime = time (NULL);
  851. }
  852. if (--file->archive->fd_usage == 0)
  853. vfs_stamp_create (&vfs_extfs_ops, file->archive);
  854. g_free (data);
  855. if (errno_code != 0)
  856. ERRNOR (EIO, -1);
  857. return 0;
  858. }
  859. /* --------------------------------------------------------------------------------------------- */
  860. static int
  861. extfs_errno (struct vfs_class *me)
  862. {
  863. (void) me;
  864. return my_errno;
  865. }
  866. /* --------------------------------------------------------------------------------------------- */
  867. static void *
  868. extfs_opendir (const vfs_path_t * vpath)
  869. {
  870. struct archive *archive = NULL;
  871. char *q;
  872. struct entry *entry;
  873. struct entry **info;
  874. q = extfs_get_path (vpath, &archive, FALSE);
  875. if (q == NULL)
  876. return NULL;
  877. entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
  878. g_free (q);
  879. if (entry == NULL)
  880. return NULL;
  881. entry = extfs_resolve_symlinks (entry);
  882. if (entry == NULL)
  883. return NULL;
  884. if (!S_ISDIR (entry->inode->mode))
  885. ERRNOR (ENOTDIR, NULL);
  886. info = g_new (struct entry *, 2);
  887. info[0] = entry->inode->first_in_subdir;
  888. info[1] = entry->inode->first_in_subdir;
  889. return info;
  890. }
  891. /* --------------------------------------------------------------------------------------------- */
  892. static void *
  893. extfs_readdir (void *data)
  894. {
  895. static union vfs_dirent dir;
  896. struct entry **info = (struct entry **) data;
  897. if (*info == NULL)
  898. return NULL;
  899. g_strlcpy (dir.dent.d_name, (*info)->name, MC_MAXPATHLEN);
  900. *info = (*info)->next_in_dir;
  901. return (void *) &dir;
  902. }
  903. /* --------------------------------------------------------------------------------------------- */
  904. static int
  905. extfs_closedir (void *data)
  906. {
  907. g_free (data);
  908. return 0;
  909. }
  910. /* --------------------------------------------------------------------------------------------- */
  911. static void
  912. extfs_stat_move (struct stat *buf, const struct inode *inode)
  913. {
  914. buf->st_dev = inode->dev;
  915. buf->st_ino = inode->inode;
  916. buf->st_mode = inode->mode;
  917. buf->st_nlink = inode->nlink;
  918. buf->st_uid = inode->uid;
  919. buf->st_gid = inode->gid;
  920. #ifdef HAVE_STRUCT_STAT_ST_RDEV
  921. buf->st_rdev = inode->rdev;
  922. #endif
  923. buf->st_size = inode->size;
  924. #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
  925. buf->st_blksize = RECORDSIZE;
  926. #endif
  927. #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
  928. buf->st_blocks = (inode->size + RECORDSIZE - 1) / RECORDSIZE;
  929. #endif
  930. buf->st_atime = inode->atime;
  931. buf->st_mtime = inode->mtime;
  932. buf->st_ctime = inode->ctime;
  933. }
  934. /* --------------------------------------------------------------------------------------------- */
  935. static int
  936. extfs_internal_stat (const vfs_path_t * vpath, struct stat *buf, gboolean resolve)
  937. {
  938. struct archive *archive;
  939. const char *q;
  940. struct entry *entry;
  941. int result = -1;
  942. q = extfs_get_path_int (vpath, &archive, FALSE);
  943. if (q == NULL)
  944. goto cleanup;
  945. entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
  946. if (entry == NULL)
  947. goto cleanup;
  948. if (resolve)
  949. {
  950. entry = extfs_resolve_symlinks (entry);
  951. if (entry == NULL)
  952. goto cleanup;
  953. }
  954. extfs_stat_move (buf, entry->inode);
  955. result = 0;
  956. cleanup:
  957. return result;
  958. }
  959. /* --------------------------------------------------------------------------------------------- */
  960. static int
  961. extfs_stat (const vfs_path_t * vpath, struct stat *buf)
  962. {
  963. return extfs_internal_stat (vpath, buf, TRUE);
  964. }
  965. /* --------------------------------------------------------------------------------------------- */
  966. static int
  967. extfs_lstat (const vfs_path_t * vpath, struct stat *buf)
  968. {
  969. return extfs_internal_stat (vpath, buf, FALSE);
  970. }
  971. /* --------------------------------------------------------------------------------------------- */
  972. static int
  973. extfs_fstat (void *data, struct stat *buf)
  974. {
  975. struct pseudofile *file = (struct pseudofile *) data;
  976. extfs_stat_move (buf, file->entry->inode);
  977. return 0;
  978. }
  979. /* --------------------------------------------------------------------------------------------- */
  980. static int
  981. extfs_readlink (const vfs_path_t * vpath, char *buf, size_t size)
  982. {
  983. struct archive *archive;
  984. const char *q;
  985. size_t len;
  986. struct entry *entry;
  987. int result = -1;
  988. q = extfs_get_path_int (vpath, &archive, FALSE);
  989. if (q == NULL)
  990. goto cleanup;
  991. entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
  992. if (entry == NULL)
  993. goto cleanup;
  994. if (!S_ISLNK (entry->inode->mode))
  995. {
  996. const vfs_path_element_t *path_element;
  997. path_element = vfs_path_get_by_index (vpath, -1);
  998. path_element->class->verrno = EINVAL;
  999. goto cleanup;
  1000. }
  1001. len = strlen (entry->inode->linkname);
  1002. if (size < len)
  1003. len = size;
  1004. /* readlink() does not append a NUL character to buf */
  1005. result = len;
  1006. memcpy (buf, entry->inode->linkname, result);
  1007. cleanup:
  1008. return result;
  1009. }
  1010. /* --------------------------------------------------------------------------------------------- */
  1011. static int
  1012. extfs_chown (const vfs_path_t * vpath, uid_t owner, gid_t group)
  1013. {
  1014. (void) vpath;
  1015. (void) owner;
  1016. (void) group;
  1017. return 0;
  1018. }
  1019. /* --------------------------------------------------------------------------------------------- */
  1020. static int
  1021. extfs_chmod (const vfs_path_t * vpath, mode_t mode)
  1022. {
  1023. (void) vpath;
  1024. (void) mode;
  1025. return 0;
  1026. }
  1027. /* --------------------------------------------------------------------------------------------- */
  1028. static ssize_t
  1029. extfs_write (void *data, const char *buf, size_t nbyte)
  1030. {
  1031. struct pseudofile *file = (struct pseudofile *) data;
  1032. file->has_changed = TRUE;
  1033. return write (file->local_handle, buf, nbyte);
  1034. }
  1035. /* --------------------------------------------------------------------------------------------- */
  1036. static int
  1037. extfs_unlink (const vfs_path_t * vpath)
  1038. {
  1039. struct archive *archive;
  1040. const char *q;
  1041. struct entry *entry;
  1042. int result = -1;
  1043. q = extfs_get_path_int (vpath, &archive, FALSE);
  1044. if (q == NULL)
  1045. goto cleanup;
  1046. entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
  1047. if (entry == NULL)
  1048. goto cleanup;
  1049. entry = extfs_resolve_symlinks (entry);
  1050. if (entry == NULL)
  1051. goto cleanup;
  1052. if (S_ISDIR (entry->inode->mode))
  1053. {
  1054. const vfs_path_element_t *path_element;
  1055. path_element = vfs_path_get_by_index (vpath, -1);
  1056. path_element->class->verrno = EISDIR;
  1057. goto cleanup;
  1058. }
  1059. if (extfs_cmd (" rm ", archive, entry, ""))
  1060. {
  1061. my_errno = EIO;
  1062. goto cleanup;
  1063. }
  1064. extfs_remove_entry (entry);
  1065. result = 0;
  1066. cleanup:
  1067. return result;
  1068. }
  1069. /* --------------------------------------------------------------------------------------------- */
  1070. static int
  1071. extfs_mkdir (const vfs_path_t * vpath, mode_t mode)
  1072. {
  1073. struct archive *archive;
  1074. const char *q;
  1075. struct entry *entry;
  1076. int result = -1;
  1077. const vfs_path_element_t *path_element;
  1078. (void) mode;
  1079. path_element = vfs_path_get_by_index (vpath, -1);
  1080. q = extfs_get_path_int (vpath, &archive, FALSE);
  1081. if (q == NULL)
  1082. goto cleanup;
  1083. entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
  1084. if (entry != NULL)
  1085. {
  1086. path_element->class->verrno = EEXIST;
  1087. goto cleanup;
  1088. }
  1089. entry = extfs_find_entry (archive->root_entry, q, TRUE, FALSE);
  1090. if (entry == NULL)
  1091. goto cleanup;
  1092. entry = extfs_resolve_symlinks (entry);
  1093. if (entry == NULL)
  1094. goto cleanup;
  1095. if (!S_ISDIR (entry->inode->mode))
  1096. {
  1097. path_element->class->verrno = ENOTDIR;
  1098. goto cleanup;
  1099. }
  1100. if (extfs_cmd (" mkdir ", archive, entry, ""))
  1101. {
  1102. my_errno = EIO;
  1103. extfs_remove_entry (entry);
  1104. goto cleanup;
  1105. }
  1106. result = 0;
  1107. cleanup:
  1108. return result;
  1109. }
  1110. /* --------------------------------------------------------------------------------------------- */
  1111. static int
  1112. extfs_rmdir (const vfs_path_t * vpath)
  1113. {
  1114. struct archive *archive;
  1115. const char *q;
  1116. struct entry *entry;
  1117. int result = -1;
  1118. q = extfs_get_path_int (vpath, &archive, FALSE);
  1119. if (q == NULL)
  1120. goto cleanup;
  1121. entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
  1122. if (entry == NULL)
  1123. goto cleanup;
  1124. entry = extfs_resolve_symlinks (entry);
  1125. if (entry == NULL)
  1126. goto cleanup;
  1127. if (!S_ISDIR (entry->inode->mode))
  1128. {
  1129. const vfs_path_element_t *path_element;
  1130. path_element = vfs_path_get_by_index (vpath, -1);
  1131. path_element->class->verrno = ENOTDIR;
  1132. goto cleanup;
  1133. }
  1134. if (extfs_cmd (" rmdir ", archive, entry, ""))
  1135. {
  1136. my_errno = EIO;
  1137. goto cleanup;
  1138. }
  1139. extfs_remove_entry (entry);
  1140. result = 0;
  1141. cleanup:
  1142. return result;
  1143. }
  1144. /* --------------------------------------------------------------------------------------------- */
  1145. static int
  1146. extfs_chdir (const vfs_path_t * vpath)
  1147. {
  1148. struct archive *archive = NULL;
  1149. char *q;
  1150. struct entry *entry;
  1151. my_errno = ENOTDIR;
  1152. q = extfs_get_path (vpath, &archive, FALSE);
  1153. if (q == NULL)
  1154. return -1;
  1155. entry = extfs_find_entry (archive->root_entry, q, FALSE, FALSE);
  1156. g_free (q);
  1157. if (entry == NULL)
  1158. return -1;
  1159. entry = extfs_resolve_symlinks (entry);
  1160. if ((entry == NULL) || (!S_ISDIR (entry->inode->mode)))
  1161. return -1;
  1162. my_errno = 0;
  1163. return 0;
  1164. }
  1165. /* --------------------------------------------------------------------------------------------- */
  1166. static off_t
  1167. extfs_lseek (void *data, off_t offset, int whence)
  1168. {
  1169. struct pseudofile *file = (struct pseudofile *) data;
  1170. return lseek (file->local_handle, offset, whence);
  1171. }
  1172. /* --------------------------------------------------------------------------------------------- */
  1173. static vfsid
  1174. extfs_getid (const vfs_path_t * vpath)
  1175. {
  1176. struct archive *archive = NULL;
  1177. char *p;
  1178. p = extfs_get_path (vpath, &archive, TRUE);
  1179. if (p == NULL)
  1180. return NULL;
  1181. g_free (p);
  1182. return (vfsid) archive;
  1183. }
  1184. /* --------------------------------------------------------------------------------------------- */
  1185. static int
  1186. extfs_nothingisopen (vfsid id)
  1187. {
  1188. return (((struct archive *) id)->fd_usage <= 0);
  1189. }
  1190. /* --------------------------------------------------------------------------------------------- */
  1191. static void
  1192. extfs_remove_entry (struct entry *e)
  1193. {
  1194. int i = --e->inode->nlink;
  1195. struct entry *pe, *ent, *prev;
  1196. if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
  1197. {
  1198. struct entry *f = e->inode->first_in_subdir;
  1199. e->inode->first_in_subdir = NULL;
  1200. extfs_remove_entry (f);
  1201. }
  1202. pe = e->dir;
  1203. if (e == pe->inode->first_in_subdir)
  1204. pe->inode->first_in_subdir = e->next_in_dir;
  1205. prev = NULL;
  1206. for (ent = pe->inode->first_in_subdir; ent && ent->next_in_dir; ent = ent->next_in_dir)
  1207. if (e == ent->next_in_dir)
  1208. {
  1209. prev = ent;
  1210. break;
  1211. }
  1212. if (prev)
  1213. prev->next_in_dir = e->next_in_dir;
  1214. if (e == pe->inode->last_in_subdir)
  1215. pe->inode->last_in_subdir = prev;
  1216. if (i <= 0)
  1217. {
  1218. if (e->inode->local_filename != NULL)
  1219. {
  1220. unlink (e->inode->local_filename);
  1221. g_free (e->inode->local_filename);
  1222. }
  1223. g_free (e->inode->linkname);
  1224. g_free (e->inode);
  1225. }
  1226. g_free (e->name);
  1227. g_free (e);
  1228. }
  1229. /* --------------------------------------------------------------------------------------------- */
  1230. static void
  1231. extfs_free_entry (struct entry *e)
  1232. {
  1233. int i = --e->inode->nlink;
  1234. if (S_ISDIR (e->inode->mode) && e->inode->first_in_subdir != NULL)
  1235. {
  1236. struct entry *f = e->inode->first_in_subdir;
  1237. e->inode->first_in_subdir = NULL;
  1238. extfs_free_entry (f);
  1239. }
  1240. if (i <= 0)
  1241. {
  1242. if (e->inode->local_filename != NULL)
  1243. {
  1244. unlink (e->inode->local_filename);
  1245. g_free (e->inode->local_filename);
  1246. }
  1247. g_free (e->inode->linkname);
  1248. g_free (e->inode);
  1249. }
  1250. if (e->next_in_dir != NULL)
  1251. extfs_free_entry (e->next_in_dir);
  1252. g_free (e->name);
  1253. g_free (e);
  1254. }
  1255. /* --------------------------------------------------------------------------------------------- */
  1256. static void
  1257. extfs_free (vfsid id)
  1258. {
  1259. struct archive *archive = (struct archive *) id;
  1260. if (archive == first_archive)
  1261. {
  1262. first_archive = archive->next;
  1263. }
  1264. else
  1265. {
  1266. struct archive *parc;
  1267. for (parc = first_archive; parc != NULL; parc = parc->next)
  1268. if (parc->next == archive)
  1269. {
  1270. parc->next = archive->next;
  1271. break;
  1272. }
  1273. }
  1274. extfs_free_archive (archive);
  1275. }
  1276. /* --------------------------------------------------------------------------------------------- */
  1277. static vfs_path_t *
  1278. extfs_getlocalcopy (const vfs_path_t * vpath)
  1279. {
  1280. struct pseudofile *fp;
  1281. vfs_path_t *p;
  1282. fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
  1283. if (fp == NULL)
  1284. return NULL;
  1285. if (fp->entry->inode->local_filename == NULL)
  1286. {
  1287. extfs_close ((void *) fp);
  1288. return NULL;
  1289. }
  1290. p = vfs_path_from_str (fp->entry->inode->local_filename);
  1291. fp->archive->fd_usage++;
  1292. extfs_close ((void *) fp);
  1293. return p;
  1294. }
  1295. /* --------------------------------------------------------------------------------------------- */
  1296. static int
  1297. extfs_ungetlocalcopy (const vfs_path_t * vpath, const vfs_path_t * local, gboolean has_changed)
  1298. {
  1299. struct pseudofile *fp;
  1300. fp = (struct pseudofile *) extfs_open (vpath, O_RDONLY, 0);
  1301. if (fp == NULL)
  1302. return 0;
  1303. if (strcmp (fp->entry->inode->local_filename, vfs_path_get_last_path_str (local)) == 0)
  1304. {
  1305. fp->archive->fd_usage--;
  1306. if (has_changed)
  1307. fp->has_changed = TRUE;
  1308. extfs_close ((void *) fp);
  1309. return 0;
  1310. }
  1311. else
  1312. {
  1313. /* Should not happen */
  1314. extfs_close ((void *) fp);
  1315. return 0;
  1316. }
  1317. }
  1318. /* --------------------------------------------------------------------------------------------- */
  1319. static gboolean
  1320. extfs_get_plugins (const char *where, gboolean silent)
  1321. {
  1322. char *dirname;
  1323. GDir *dir;
  1324. const char *filename;
  1325. dirname = g_build_path (PATH_SEP_STR, where, MC_EXTFS_DIR, (char *) NULL);
  1326. dir = g_dir_open (dirname, 0, NULL);
  1327. /* We may not use vfs_die() message or message or similar,
  1328. * UI is not initialized at this time and message would not
  1329. * appear on screen. */
  1330. if (dir == NULL)
  1331. {
  1332. if (!silent)
  1333. fprintf (stderr, _("Warning: cannot open %s directory\n"), dirname);
  1334. g_free (dirname);
  1335. return FALSE;
  1336. }
  1337. if (extfs_plugins == NULL)
  1338. extfs_plugins = g_array_sized_new (FALSE, TRUE, sizeof (extfs_plugin_info_t), 32);
  1339. while ((filename = g_dir_read_name (dir)) != NULL)
  1340. {
  1341. char fullname[MC_MAXPATHLEN];
  1342. struct stat s;
  1343. g_snprintf (fullname, sizeof (fullname), "%s" PATH_SEP_STR "%s", dirname, filename);
  1344. if ((stat (fullname, &s) == 0)
  1345. && S_ISREG (s.st_mode) && !S_ISDIR (s.st_mode)
  1346. && (((s.st_mode & S_IXOTH) != 0) ||
  1347. ((s.st_mode & S_IXUSR) != 0) || ((s.st_mode & S_IXGRP) != 0)))
  1348. {
  1349. int f;
  1350. f = open (fullname, O_RDONLY);
  1351. if (f >= 0)
  1352. {
  1353. size_t len, i;
  1354. extfs_plugin_info_t info;
  1355. gboolean found = FALSE;
  1356. close (f);
  1357. /* Handle those with a trailing '+', those flag that the
  1358. * file system does not require an archive to work
  1359. */
  1360. len = strlen (filename);
  1361. info.need_archive = (filename[len - 1] != '+');
  1362. info.path = g_strconcat (dirname, PATH_SEP_STR, (char *) NULL);
  1363. info.prefix = g_strdup (filename);
  1364. /* prepare to compare file names without trailing '+' */
  1365. if (!info.need_archive)
  1366. info.prefix[len - 1] = '\0';
  1367. /* don't overload already found plugin */
  1368. for (i = 0; i < extfs_plugins->len; i++)
  1369. {
  1370. extfs_plugin_info_t *p;
  1371. p = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
  1372. /* 2 files with same names cannot be in a directory */
  1373. if ((strcmp (info.path, p->path) != 0)
  1374. && (strcmp (info.prefix, p->prefix) == 0))
  1375. {
  1376. found = TRUE;
  1377. break;
  1378. }
  1379. }
  1380. if (found)
  1381. {
  1382. g_free (info.path);
  1383. g_free (info.prefix);
  1384. }
  1385. else
  1386. {
  1387. /* restore file name */
  1388. if (!info.need_archive)
  1389. info.prefix[len - 1] = '+';
  1390. g_array_append_val (extfs_plugins, info);
  1391. }
  1392. }
  1393. }
  1394. }
  1395. g_dir_close (dir);
  1396. g_free (dirname);
  1397. return TRUE;
  1398. }
  1399. /* --------------------------------------------------------------------------------------------- */
  1400. static int
  1401. extfs_init (struct vfs_class *me)
  1402. {
  1403. gboolean d1, d2;
  1404. (void) me;
  1405. /* 1st: scan user directory */
  1406. d1 = extfs_get_plugins (mc_config_get_data_path (), TRUE); /* silent about user dir */
  1407. /* 2nd: scan system dir */
  1408. d2 = extfs_get_plugins (LIBEXECDIR, d1);
  1409. return (d1 || d2 ? 1 : 0);
  1410. }
  1411. /* --------------------------------------------------------------------------------------------- */
  1412. static void
  1413. extfs_done (struct vfs_class *me)
  1414. {
  1415. size_t i;
  1416. struct archive *ar;
  1417. (void) me;
  1418. for (ar = first_archive; ar != NULL;)
  1419. {
  1420. extfs_free ((vfsid) ar);
  1421. ar = first_archive;
  1422. }
  1423. for (i = 0; i < extfs_plugins->len; i++)
  1424. {
  1425. extfs_plugin_info_t *info;
  1426. info = &g_array_index (extfs_plugins, extfs_plugin_info_t, i);
  1427. g_free (info->path);
  1428. g_free (info->prefix);
  1429. }
  1430. if (extfs_plugins != NULL)
  1431. g_array_free (extfs_plugins, TRUE);
  1432. }
  1433. /* --------------------------------------------------------------------------------------------- */
  1434. static int
  1435. extfs_setctl (const vfs_path_t * vpath, int ctlop, void *arg)
  1436. {
  1437. (void) arg;
  1438. if (ctlop == VFS_SETCTL_RUN)
  1439. {
  1440. extfs_run (vpath);
  1441. return 1;
  1442. }
  1443. return 0;
  1444. }
  1445. /* --------------------------------------------------------------------------------------------- */
  1446. /*** public functions ****************************************************************************/
  1447. /* --------------------------------------------------------------------------------------------- */
  1448. void
  1449. init_extfs (void)
  1450. {
  1451. vfs_extfs_ops.name = "extfs";
  1452. vfs_extfs_ops.init = extfs_init;
  1453. vfs_extfs_ops.done = extfs_done;
  1454. vfs_extfs_ops.fill_names = extfs_fill_names;
  1455. vfs_extfs_ops.which = extfs_which;
  1456. vfs_extfs_ops.open = extfs_open;
  1457. vfs_extfs_ops.close = extfs_close;
  1458. vfs_extfs_ops.read = extfs_read;
  1459. vfs_extfs_ops.write = extfs_write;
  1460. vfs_extfs_ops.opendir = extfs_opendir;
  1461. vfs_extfs_ops.readdir = extfs_readdir;
  1462. vfs_extfs_ops.closedir = extfs_closedir;
  1463. vfs_extfs_ops.stat = extfs_stat;
  1464. vfs_extfs_ops.lstat = extfs_lstat;
  1465. vfs_extfs_ops.fstat = extfs_fstat;
  1466. vfs_extfs_ops.chmod = extfs_chmod;
  1467. vfs_extfs_ops.chown = extfs_chown;
  1468. vfs_extfs_ops.readlink = extfs_readlink;
  1469. vfs_extfs_ops.unlink = extfs_unlink;
  1470. vfs_extfs_ops.chdir = extfs_chdir;
  1471. vfs_extfs_ops.ferrno = extfs_errno;
  1472. vfs_extfs_ops.lseek = extfs_lseek;
  1473. vfs_extfs_ops.getid = extfs_getid;
  1474. vfs_extfs_ops.nothingisopen = extfs_nothingisopen;
  1475. vfs_extfs_ops.free = extfs_free;
  1476. vfs_extfs_ops.getlocalcopy = extfs_getlocalcopy;
  1477. vfs_extfs_ops.ungetlocalcopy = extfs_ungetlocalcopy;
  1478. vfs_extfs_ops.mkdir = extfs_mkdir;
  1479. vfs_extfs_ops.rmdir = extfs_rmdir;
  1480. vfs_extfs_ops.setctl = extfs_setctl;
  1481. vfs_register_class (&vfs_extfs_ops);
  1482. }
  1483. /* --------------------------------------------------------------------------------------------- */