vfs.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. /*
  2. Virtual File System switch code
  3. Copyright (C) 1995-2024
  4. Free Software Foundation, Inc.
  5. Written by: 1995 Miguel de Icaza
  6. Jakub Jelinek, 1995
  7. Pavel Machek, 1998
  8. Slava Zanko <slavazanko@gmail.com>, 2011-2013
  9. Andrew Borodin <aborodin@vmail.ru>, 2011-2022
  10. This file is part of the Midnight Commander.
  11. The Midnight Commander is free software: you can redistribute it
  12. and/or modify it under the terms of the GNU General Public License as
  13. published by the Free Software Foundation, either version 3 of the License,
  14. or (at your option) any later version.
  15. The Midnight Commander is distributed in the hope that it will be useful,
  16. but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. GNU General Public License for more details.
  19. You should have received a copy of the GNU General Public License
  20. along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. */
  22. /**
  23. * \file
  24. * \brief Source: Virtual File System switch code
  25. * \author Miguel de Icaza
  26. * \author Jakub Jelinek
  27. * \author Pavel Machek
  28. * \date 1995, 1998
  29. * \warning functions like extfs_lstat() have right to destroy any
  30. * strings you pass to them. This is actually ok as you g_strdup what
  31. * you are passing to them, anyway; still, beware.
  32. *
  33. * Namespace: exports *many* functions with vfs_ prefix; exports
  34. * parse_ls_lga and friends which do not have that prefix.
  35. */
  36. #include <config.h>
  37. #include <errno.h>
  38. #include <stdlib.h>
  39. #ifdef __linux__
  40. #ifdef HAVE_LINUX_FS_H
  41. #include <linux/fs.h>
  42. #endif /* HAVE_LINUX_FS_H */
  43. #ifdef HAVE_SYS_IOCTL_H
  44. #include <sys/ioctl.h>
  45. #endif /* HAVE_SYS_IOCTL_H */
  46. #endif /* __linux__ */
  47. #include "lib/global.h"
  48. #include "lib/strutil.h"
  49. #include "lib/util.h"
  50. #include "lib/widget.h" /* message() */
  51. #include "lib/event.h"
  52. #ifdef HAVE_CHARSET
  53. #include "lib/charsets.h"
  54. #endif
  55. #include "vfs.h"
  56. #include "utilvfs.h"
  57. #include "gc.h"
  58. /* TODO: move it to the separate .h */
  59. extern struct vfs_dirent *mc_readdir_result;
  60. extern GPtrArray *vfs__classes_list;
  61. extern GString *vfs_str_buffer;
  62. extern vfs_class *current_vfs;
  63. /*** global variables ****************************************************************************/
  64. struct vfs_dirent *mc_readdir_result = NULL;
  65. GPtrArray *vfs__classes_list = NULL;
  66. GString *vfs_str_buffer = NULL;
  67. vfs_class *current_vfs = NULL;
  68. /*** file scope macro definitions ****************************************************************/
  69. #define VFS_FIRST_HANDLE 100
  70. /*** file scope type declarations ****************************************************************/
  71. struct vfs_openfile
  72. {
  73. int handle;
  74. vfs_class *vclass;
  75. void *fsinfo;
  76. };
  77. /*** forward declarations (file scope functions) *************************************************/
  78. /*** file scope variables ************************************************************************/
  79. /** They keep track of the current directory */
  80. static vfs_path_t *current_path = NULL;
  81. static GPtrArray *vfs_openfiles = NULL;
  82. static long vfs_free_handle_list = -1;
  83. /* --------------------------------------------------------------------------------------------- */
  84. /*** file scope functions ************************************************************************/
  85. /* --------------------------------------------------------------------------------------------- */
  86. /* now used only by vfs_translate_path, but could be used in other vfs
  87. * plugin to automatic detect encoding
  88. * path - path to translate
  89. * size - how many bytes from path translate
  90. * defcnv - converter, that is used as default, when path does not contain any
  91. * #enc: substring
  92. * buffer - used to store result of translation
  93. */
  94. static estr_t
  95. _vfs_translate_path (const char *path, int size, GIConv defcnv, GString *buffer)
  96. {
  97. estr_t state = ESTR_SUCCESS;
  98. #ifdef HAVE_CHARSET
  99. const char *semi;
  100. if (size == 0)
  101. return ESTR_SUCCESS;
  102. size = (size > 0) ? size : (signed int) strlen (path);
  103. /* try found /#enc: */
  104. semi = g_strrstr_len (path, size, VFS_ENCODING_PREFIX);
  105. if (semi != NULL && (semi == path || IS_PATH_SEP (semi[-1])))
  106. {
  107. char encoding[16];
  108. const char *slash;
  109. GIConv coder = INVALID_CONV;
  110. int ms;
  111. /* first must be translated part before #enc: */
  112. ms = semi - path;
  113. state = _vfs_translate_path (path, ms, defcnv, buffer);
  114. if (state != ESTR_SUCCESS)
  115. return state;
  116. /* now can be translated part after #enc: */
  117. semi += strlen (VFS_ENCODING_PREFIX); /* skip "#enc:" */
  118. slash = strchr (semi, PATH_SEP);
  119. /* ignore slashes after size; */
  120. if (slash - path >= size)
  121. slash = NULL;
  122. ms = (slash != NULL) ? slash - semi : (int) strlen (semi);
  123. ms = MIN ((unsigned int) ms, sizeof (encoding) - 1);
  124. /* limit encoding size (ms) to path size (size) */
  125. if (semi + ms > path + size)
  126. ms = path + size - semi;
  127. memcpy (encoding, semi, ms);
  128. encoding[ms] = '\0';
  129. if (is_supported_encoding (encoding))
  130. coder = str_crt_conv_to (encoding);
  131. if (coder != INVALID_CONV)
  132. {
  133. if (slash != NULL)
  134. state = str_vfs_convert_to (coder, slash + 1, path + size - slash - 1, buffer);
  135. str_close_conv (coder);
  136. return state;
  137. }
  138. errno = EINVAL;
  139. state = ESTR_FAILURE;
  140. }
  141. else
  142. {
  143. /* path can be translated whole at once */
  144. state = str_vfs_convert_to (defcnv, path, size, buffer);
  145. }
  146. #else
  147. (void) size;
  148. (void) defcnv;
  149. g_string_assign (buffer, path);
  150. #endif /* HAVE_CHARSET */
  151. return state;
  152. }
  153. /* --------------------------------------------------------------------------------------------- */
  154. static struct vfs_openfile *
  155. vfs_get_openfile (int handle)
  156. {
  157. struct vfs_openfile *h;
  158. if (handle < VFS_FIRST_HANDLE || (guint) (handle - VFS_FIRST_HANDLE) >= vfs_openfiles->len)
  159. return NULL;
  160. h = (struct vfs_openfile *) g_ptr_array_index (vfs_openfiles, handle - VFS_FIRST_HANDLE);
  161. if (h == NULL)
  162. return NULL;
  163. g_assert (h->handle == handle);
  164. return h;
  165. }
  166. /* --------------------------------------------------------------------------------------------- */
  167. static gboolean
  168. vfs_test_current_dir (const vfs_path_t *vpath)
  169. {
  170. struct stat my_stat, my_stat2;
  171. return (mc_global.vfs.cd_symlinks && mc_stat (vpath, &my_stat) == 0
  172. && mc_stat (vfs_get_raw_current_dir (), &my_stat2) == 0
  173. && my_stat.st_ino == my_stat2.st_ino && my_stat.st_dev == my_stat2.st_dev);
  174. }
  175. /* --------------------------------------------------------------------------------------------- */
  176. /*** public functions ****************************************************************************/
  177. /* --------------------------------------------------------------------------------------------- */
  178. /** Free open file data for given file handle */
  179. void
  180. vfs_free_handle (int handle)
  181. {
  182. const int idx = handle - VFS_FIRST_HANDLE;
  183. if (handle >= VFS_FIRST_HANDLE && (guint) idx < vfs_openfiles->len)
  184. {
  185. struct vfs_openfile *h;
  186. h = (struct vfs_openfile *) g_ptr_array_index (vfs_openfiles, idx);
  187. g_free (h);
  188. g_ptr_array_index (vfs_openfiles, idx) = (void *) vfs_free_handle_list;
  189. vfs_free_handle_list = idx;
  190. }
  191. }
  192. /* --------------------------------------------------------------------------------------------- */
  193. /** Find VFS class by file handle */
  194. struct vfs_class *
  195. vfs_class_find_by_handle (int handle, void **fsinfo)
  196. {
  197. struct vfs_openfile *h;
  198. h = vfs_get_openfile (handle);
  199. if (h == NULL)
  200. return NULL;
  201. if (fsinfo != NULL)
  202. *fsinfo = h->fsinfo;
  203. return h->vclass;
  204. }
  205. /* --------------------------------------------------------------------------------------------- */
  206. /**
  207. * Create new VFS handle and put it to the list
  208. */
  209. int
  210. vfs_new_handle (struct vfs_class *vclass, void *fsinfo)
  211. {
  212. struct vfs_openfile *h;
  213. h = g_new (struct vfs_openfile, 1);
  214. h->fsinfo = fsinfo;
  215. h->vclass = vclass;
  216. /* Allocate the first free handle */
  217. h->handle = vfs_free_handle_list;
  218. if (h->handle == -1)
  219. {
  220. /* No free allocated handles, allocate one */
  221. h->handle = vfs_openfiles->len;
  222. g_ptr_array_add (vfs_openfiles, h);
  223. }
  224. else
  225. {
  226. vfs_free_handle_list = (long) g_ptr_array_index (vfs_openfiles, vfs_free_handle_list);
  227. g_ptr_array_index (vfs_openfiles, h->handle) = h;
  228. }
  229. h->handle += VFS_FIRST_HANDLE;
  230. return h->handle;
  231. }
  232. /* --------------------------------------------------------------------------------------------- */
  233. int
  234. vfs_ferrno (struct vfs_class *vfs)
  235. {
  236. return vfs->ferrno ? (*vfs->ferrno) (vfs) : E_UNKNOWN;
  237. /* Hope that error message is obscure enough ;-) */
  238. }
  239. /* --------------------------------------------------------------------------------------------- */
  240. gboolean
  241. vfs_register_class (struct vfs_class *vfs)
  242. {
  243. if (vfs->init != NULL) /* vfs has own initialization function */
  244. if (!vfs->init (vfs)) /* but it failed */
  245. return FALSE;
  246. g_ptr_array_add (vfs__classes_list, vfs);
  247. return TRUE;
  248. }
  249. /* --------------------------------------------------------------------------------------------- */
  250. void
  251. vfs_unregister_class (struct vfs_class *vfs)
  252. {
  253. if (vfs->done != NULL)
  254. vfs->done (vfs);
  255. g_ptr_array_remove (vfs__classes_list, vfs);
  256. }
  257. /* --------------------------------------------------------------------------------------------- */
  258. /** Strip known vfs suffixes from a filename (possible improvement: strip
  259. * suffix from last path component).
  260. * \return a malloced string which has to be freed.
  261. */
  262. char *
  263. vfs_strip_suffix_from_filename (const char *filename)
  264. {
  265. char *semi, *p;
  266. if (filename == NULL)
  267. vfs_die ("vfs_strip_suffix_from_path got NULL: impossible");
  268. p = g_strdup (filename);
  269. semi = g_strrstr (p, VFS_PATH_URL_DELIMITER);
  270. if (semi != NULL)
  271. {
  272. char *vfs_prefix;
  273. *semi = '\0';
  274. vfs_prefix = strrchr (p, PATH_SEP);
  275. if (vfs_prefix == NULL)
  276. *semi = *VFS_PATH_URL_DELIMITER;
  277. else
  278. *vfs_prefix = '\0';
  279. }
  280. return p;
  281. }
  282. /* --------------------------------------------------------------------------------------------- */
  283. const char *
  284. vfs_translate_path (const char *path)
  285. {
  286. estr_t state;
  287. g_string_set_size (vfs_str_buffer, 0);
  288. state = _vfs_translate_path (path, -1, str_cnv_from_term, vfs_str_buffer);
  289. return (state != ESTR_FAILURE) ? vfs_str_buffer->str : NULL;
  290. }
  291. /* --------------------------------------------------------------------------------------------- */
  292. char *
  293. vfs_translate_path_n (const char *path)
  294. {
  295. const char *result;
  296. result = vfs_translate_path (path);
  297. return g_strdup (result);
  298. }
  299. /* --------------------------------------------------------------------------------------------- */
  300. /**
  301. * Get current directory without any OS calls.
  302. *
  303. * @return string contains current path
  304. */
  305. const char *
  306. vfs_get_current_dir (void)
  307. {
  308. return current_path->str;
  309. }
  310. /* --------------------------------------------------------------------------------------------- */
  311. /**
  312. * Get current directory without any OS calls.
  313. *
  314. * @return newly allocated string contains current path
  315. */
  316. char *
  317. vfs_get_current_dir_n (void)
  318. {
  319. return g_strdup (current_path->str);
  320. }
  321. /* --------------------------------------------------------------------------------------------- */
  322. /**
  323. * Get raw current directory object without any OS calls.
  324. *
  325. * @return object contain current path
  326. */
  327. const vfs_path_t *
  328. vfs_get_raw_current_dir (void)
  329. {
  330. return current_path;
  331. }
  332. /* --------------------------------------------------------------------------------------------- */
  333. /**
  334. * Set current directory object.
  335. *
  336. * @param vpath new path
  337. */
  338. void
  339. vfs_set_raw_current_dir (const vfs_path_t *vpath)
  340. {
  341. vfs_path_free (current_path, TRUE);
  342. current_path = (vfs_path_t *) vpath;
  343. }
  344. /* --------------------------------------------------------------------------------------------- */
  345. /* Return TRUE is the current VFS class is local */
  346. gboolean
  347. vfs_current_is_local (void)
  348. {
  349. return (current_vfs->flags & VFSF_LOCAL) != 0;
  350. }
  351. /* --------------------------------------------------------------------------------------------- */
  352. /* Return flags of the VFS class of the given filename */
  353. vfs_flags_t
  354. vfs_file_class_flags (const vfs_path_t *vpath)
  355. {
  356. const vfs_path_element_t *path_element;
  357. path_element = vfs_path_get_by_index (vpath, -1);
  358. if (!vfs_path_element_valid (path_element))
  359. return VFSF_UNKNOWN;
  360. return path_element->class->flags;
  361. }
  362. /* --------------------------------------------------------------------------------------------- */
  363. void
  364. vfs_init (void)
  365. {
  366. /* create the VFS handle arrays */
  367. vfs__classes_list = g_ptr_array_new ();
  368. /* create the VFS handle array */
  369. vfs_openfiles = g_ptr_array_new ();
  370. vfs_str_buffer = g_string_new ("");
  371. mc_readdir_result = vfs_dirent_init (NULL, "", -1);
  372. }
  373. /* --------------------------------------------------------------------------------------------- */
  374. void
  375. vfs_setup_work_dir (void)
  376. {
  377. vfs_setup_cwd ();
  378. /* FIXME: is we really need for this check? */
  379. /*
  380. if (strlen (current_dir) > MC_MAXPATHLEN - 2)
  381. vfs_die ("Current dir too long.\n");
  382. */
  383. current_vfs = VFS_CLASS (vfs_path_get_last_path_vfs (current_path));
  384. }
  385. /* --------------------------------------------------------------------------------------------- */
  386. void
  387. vfs_shut (void)
  388. {
  389. guint i;
  390. vfs_gc_done ();
  391. vfs_set_raw_current_dir (NULL);
  392. for (i = 0; i < vfs__classes_list->len; i++)
  393. {
  394. struct vfs_class *vfs = VFS_CLASS (g_ptr_array_index (vfs__classes_list, i));
  395. if (vfs->done != NULL)
  396. vfs->done (vfs);
  397. }
  398. /* NULL-ize pointers to make unit tests happy */
  399. g_ptr_array_free (vfs_openfiles, TRUE);
  400. vfs_openfiles = NULL;
  401. g_ptr_array_free (vfs__classes_list, TRUE);
  402. vfs__classes_list = NULL;
  403. g_string_free (vfs_str_buffer, TRUE);
  404. vfs_str_buffer = NULL;
  405. current_vfs = NULL;
  406. vfs_free_handle_list = -1;
  407. vfs_dirent_free (mc_readdir_result);
  408. mc_readdir_result = NULL;
  409. }
  410. /* --------------------------------------------------------------------------------------------- */
  411. /**
  412. * Init or create vfs_dirent structure
  413. *
  414. * @d vfs_dirent structure to init. If NULL, new structure is created.
  415. * @fname file name
  416. * @ino file inode number
  417. *
  418. * @return pointer to d if d isn't NULL, or pointer to newly created structure.
  419. */
  420. struct vfs_dirent *
  421. vfs_dirent_init (struct vfs_dirent *d, const char *fname, ino_t ino)
  422. {
  423. struct vfs_dirent *ret = d;
  424. if (ret == NULL)
  425. ret = g_new0 (struct vfs_dirent, 1);
  426. if (ret->d_name_str == NULL)
  427. ret->d_name_str = g_string_sized_new (MC_MAXFILENAMELEN);
  428. vfs_dirent_assign (ret, fname, ino);
  429. return ret;
  430. }
  431. /* --------------------------------------------------------------------------------------------- */
  432. /**
  433. * Assign members of vfs_dirent structure
  434. *
  435. * @d vfs_dirent structure for assignment
  436. * @fname file name
  437. * @ino file inode number
  438. */
  439. void
  440. vfs_dirent_assign (struct vfs_dirent *d, const char *fname, ino_t ino)
  441. {
  442. g_string_assign (d->d_name_str, fname);
  443. d->d_name = d->d_name_str->str;
  444. d->d_len = d->d_name_str->len;
  445. d->d_ino = ino;
  446. }
  447. /* --------------------------------------------------------------------------------------------- */
  448. /**
  449. * Destroy vfs_dirent structure
  450. *
  451. * @d vfs_dirent structure to destroy.
  452. */
  453. void
  454. vfs_dirent_free (struct vfs_dirent *d)
  455. {
  456. g_string_free (d->d_name_str, TRUE);
  457. g_free (d);
  458. }
  459. /* --------------------------------------------------------------------------------------------- */
  460. /**
  461. * These ones grab information from the VFS
  462. * and handles them to an upper layer
  463. */
  464. void
  465. vfs_fill_names (fill_names_f func)
  466. {
  467. guint i;
  468. for (i = 0; i < vfs__classes_list->len; i++)
  469. {
  470. struct vfs_class *vfs = VFS_CLASS (g_ptr_array_index (vfs__classes_list, i));
  471. if (vfs->fill_names != NULL)
  472. vfs->fill_names (vfs, func);
  473. }
  474. }
  475. /* --------------------------------------------------------------------------------------------- */
  476. gboolean
  477. vfs_file_is_local (const vfs_path_t *vpath)
  478. {
  479. return (vfs_file_class_flags (vpath) & VFSF_LOCAL) != 0;
  480. }
  481. /* --------------------------------------------------------------------------------------------- */
  482. void
  483. vfs_print_message (const char *msg, ...)
  484. {
  485. ev_vfs_print_message_t event_data;
  486. va_list ap;
  487. va_start (ap, msg);
  488. event_data.msg = g_strdup_vprintf (msg, ap);
  489. va_end (ap);
  490. mc_event_raise (MCEVENT_GROUP_CORE, "vfs_print_message", (gpointer) &event_data);
  491. }
  492. /* --------------------------------------------------------------------------------------------- */
  493. /**
  494. * If it's local, reread the current directory
  495. * from the OS.
  496. */
  497. void
  498. vfs_setup_cwd (void)
  499. {
  500. char *current_dir;
  501. vfs_path_t *tmp_vpath;
  502. const struct vfs_class *me;
  503. if (vfs_get_raw_current_dir () == NULL)
  504. {
  505. current_dir = g_get_current_dir ();
  506. vfs_set_raw_current_dir (vfs_path_from_str (current_dir));
  507. g_free (current_dir);
  508. current_dir = getenv ("PWD");
  509. tmp_vpath = vfs_path_from_str (current_dir);
  510. if (tmp_vpath != NULL)
  511. {
  512. if (vfs_test_current_dir (tmp_vpath))
  513. vfs_set_raw_current_dir (tmp_vpath);
  514. else
  515. vfs_path_free (tmp_vpath, TRUE);
  516. }
  517. }
  518. me = vfs_path_get_last_path_vfs (vfs_get_raw_current_dir ());
  519. if ((me->flags & VFSF_LOCAL) != 0)
  520. {
  521. current_dir = g_get_current_dir ();
  522. tmp_vpath = vfs_path_from_str (current_dir);
  523. g_free (current_dir);
  524. if (tmp_vpath != NULL)
  525. {
  526. /* One of directories in the path is not readable */
  527. /* Check if it is O.K. to use the current_dir */
  528. if (!vfs_test_current_dir (tmp_vpath))
  529. vfs_set_raw_current_dir (tmp_vpath);
  530. else
  531. vfs_path_free (tmp_vpath, TRUE);
  532. }
  533. }
  534. }
  535. /* --------------------------------------------------------------------------------------------- */
  536. /**
  537. * Return current directory. If it's local, reread the current directory
  538. * from the OS.
  539. */
  540. char *
  541. vfs_get_cwd (void)
  542. {
  543. vfs_setup_cwd ();
  544. return vfs_get_current_dir_n ();
  545. }
  546. /* --------------------------------------------------------------------------------------------- */
  547. /**
  548. * Preallocate space for file in new place for ensure that file
  549. * will be fully copied with less fragmentation.
  550. *
  551. * @param dest_vfs_fd mc VFS file handler
  552. * @param src_fsize source file size
  553. * @param dest_fsize destination file size (if destination exists, otherwise should be 0)
  554. *
  555. * @return 0 if success and non-zero otherwise.
  556. * Note: function doesn't touch errno global variable.
  557. */
  558. int
  559. vfs_preallocate (int dest_vfs_fd, off_t src_fsize, off_t dest_fsize)
  560. {
  561. #ifndef HAVE_POSIX_FALLOCATE
  562. (void) dest_vfs_fd;
  563. (void) src_fsize;
  564. (void) dest_fsize;
  565. return 0;
  566. #else /* HAVE_POSIX_FALLOCATE */
  567. void *dest_fd = NULL;
  568. struct vfs_class *dest_class;
  569. if (src_fsize == 0)
  570. return 0;
  571. dest_class = vfs_class_find_by_handle (dest_vfs_fd, &dest_fd);
  572. if ((dest_class->flags & VFSF_LOCAL) == 0 || dest_fd == NULL)
  573. return 0;
  574. return posix_fallocate (*(int *) dest_fd, dest_fsize, src_fsize - dest_fsize);
  575. #endif /* HAVE_POSIX_FALLOCATE */
  576. }
  577. /* --------------------------------------------------------------------------------------------- */
  578. int
  579. vfs_clone_file (int dest_vfs_fd, int src_vfs_fd)
  580. {
  581. #ifdef FICLONE
  582. void *dest_fd = NULL;
  583. void *src_fd = NULL;
  584. struct vfs_class *dest_class;
  585. struct vfs_class *src_class;
  586. dest_class = vfs_class_find_by_handle (dest_vfs_fd, &dest_fd);
  587. if ((dest_class->flags & VFSF_LOCAL) == 0)
  588. {
  589. errno = ENOTSUP;
  590. return (-1);
  591. }
  592. if (dest_fd == NULL)
  593. {
  594. errno = EBADF;
  595. return (-1);
  596. }
  597. src_class = vfs_class_find_by_handle (src_vfs_fd, &src_fd);
  598. if ((src_class->flags & VFSF_LOCAL) == 0)
  599. {
  600. errno = ENOTSUP;
  601. return (-1);
  602. }
  603. if (src_fd == NULL)
  604. {
  605. errno = EBADF;
  606. return (-1);
  607. }
  608. return ioctl (*(int *) dest_fd, FICLONE, *(int *) src_fd);
  609. #else
  610. (void) dest_vfs_fd;
  611. (void) src_vfs_fd;
  612. errno = ENOTSUP;
  613. return (-1);
  614. #endif
  615. }
  616. /* --------------------------------------------------------------------------------------------- */