extfs.c 35 KB

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