extfs.c 35 KB

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