extfs.c 47 KB

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