extfs.c 42 KB

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