/core/10.5/fusefs/fuse_vnops.c

http://macfuse.googlecode.com/ · C · 3792 lines · 2329 code · 703 blank · 760 comment · 498 complexity · 9ac337dac621a0b2874afb66ce10d9ae MD5 · raw file

Large files are truncated click here to view the full file

  1. /*
  2. * Copyright (C) 2006-2008 Google. All Rights Reserved.
  3. * Amit Singh <singh@>
  4. */
  5. #include <sys/param.h>
  6. #include <kern/assert.h>
  7. #include <libkern/libkern.h>
  8. #include <libkern/OSMalloc.h>
  9. #include <libkern/locks.h>
  10. #include <mach/mach_types.h>
  11. #include <sys/dirent.h>
  12. #include <sys/disk.h>
  13. #include <sys/errno.h>
  14. #include <sys/fcntl.h>
  15. #include <sys/kernel_types.h>
  16. #include <sys/mount.h>
  17. #include <sys/proc.h>
  18. #include <sys/stat.h>
  19. #include <sys/ubc.h>
  20. #include <sys/unistd.h>
  21. #include <sys/vnode.h>
  22. #include <sys/vnode_if.h>
  23. #include <sys/xattr.h>
  24. #include <sys/buf.h>
  25. #include <sys/namei.h>
  26. #include <sys/mman.h>
  27. #include <vfs/vfs_support.h>
  28. #include "fuse.h"
  29. #include "fuse_file.h"
  30. #include "fuse_internal.h"
  31. #include <fuse_ioctl.h>
  32. #include "fuse_ipc.h"
  33. #include "fuse_kludges.h"
  34. #include "fuse_knote.h"
  35. #include "fuse_locking.h"
  36. #include "fuse_node.h"
  37. #include "fuse_nodehash.h"
  38. #include <fuse_param.h>
  39. #include "fuse_sysctl.h"
  40. #include "fuse_vnops.h"
  41. /*
  42. struct vnop_access_args {
  43. struct vnodeop_desc *a_desc;
  44. vnode_t a_vp;
  45. int a_action;
  46. vfs_context_t a_context;
  47. };
  48. */
  49. FUSE_VNOP_EXPORT
  50. int
  51. fuse_vnop_access(struct vnop_access_args *ap)
  52. {
  53. vnode_t vp = ap->a_vp;
  54. int action = ap->a_action;
  55. vfs_context_t context = ap->a_context;
  56. struct fuse_access_param facp;
  57. struct fuse_vnode_data *fvdat = VTOFUD(vp);
  58. struct fuse_data *data = fuse_get_mpdata(vnode_mount(vp));
  59. fuse_trace_printf_vnop();
  60. if (fuse_isdeadfs(vp)) {
  61. if (vnode_isvroot(vp)) {
  62. return 0;
  63. } else {
  64. return EBADF;
  65. }
  66. }
  67. if (!(data->dataflags & FSESS_INITED)) {
  68. if (vnode_isvroot(vp)) {
  69. if (fuse_vfs_context_issuser(context) ||
  70. (fuse_match_cred(data->daemoncred,
  71. vfs_context_ucred(context)) == 0)) {
  72. return 0;
  73. }
  74. }
  75. return EBADF;
  76. }
  77. if (vnode_islnk(vp)) {
  78. return 0;
  79. }
  80. bzero(&facp, sizeof(facp));
  81. if (fvdat->flag & FN_ACCESS_NOOP) {
  82. fvdat->flag &= ~FN_ACCESS_NOOP;
  83. } else {
  84. facp.facc_flags |= FACCESS_DO_ACCESS;
  85. }
  86. facp.facc_flags |= FACCESS_FROM_VNOP;
  87. return fuse_internal_access(vp, action, context, &facp);
  88. }
  89. /*
  90. struct vnop_blktooff_args {
  91. struct vnodeop_desc *a_desc;
  92. vnode_t a_vp;
  93. daddr64_t a_lblkno;
  94. off_t *a_offset;
  95. };
  96. */
  97. FUSE_VNOP_EXPORT
  98. int
  99. fuse_vnop_blktooff(struct vnop_blktooff_args *ap)
  100. {
  101. vnode_t vp = ap->a_vp;
  102. daddr64_t lblkno = ap->a_lblkno;
  103. off_t *offsetPtr = ap->a_offset;
  104. struct fuse_data *data;
  105. fuse_trace_printf_vnop();
  106. if (fuse_isdeadfs(vp)) {
  107. return EIO;
  108. }
  109. data = fuse_get_mpdata(vnode_mount(vp));
  110. *offsetPtr = lblkno * (off_t)(data->blocksize);
  111. return 0;
  112. }
  113. /*
  114. struct vnop_blockmap_args {
  115. struct vnodeop_desc *a_desc;
  116. vnode_t a_vp;
  117. off_t a_foffset;
  118. size_t a_size;
  119. daddr64_t *a_bpn;
  120. size_t *a_run;
  121. void *a_poff;
  122. int a_flags;
  123. vfs_context_t a_context;
  124. };
  125. */
  126. FUSE_VNOP_EXPORT
  127. int
  128. fuse_vnop_blockmap(struct vnop_blockmap_args *ap)
  129. {
  130. vnode_t vp = ap->a_vp;
  131. off_t foffset = ap->a_foffset;
  132. size_t size = ap->a_size;
  133. daddr64_t *bpnPtr = ap->a_bpn;
  134. size_t *runPtr = ap->a_run;
  135. int *poffPtr = (int *)ap->a_poff;
  136. /* Ignoring flags and context */
  137. struct fuse_vnode_data *fvdat;
  138. struct fuse_data *data;
  139. off_t contiguousPhysicalBytes;
  140. fuse_trace_printf_vnop();
  141. if (fuse_isdeadfs(vp)) {
  142. return EIO;
  143. }
  144. if (vnode_isdir(vp)) {
  145. return ENOTSUP;
  146. }
  147. if (ap->a_bpn == NULL) {
  148. return 0;
  149. }
  150. fvdat = VTOFUD(vp);
  151. data = fuse_get_mpdata(vnode_mount(vp));
  152. /*
  153. * We could assert that:
  154. *
  155. * (foffset % data->blocksize) == 0
  156. * (foffset < fvdat->filesize)
  157. * (size % data->blocksize) == 0)
  158. */
  159. *bpnPtr = foffset / data->blocksize;
  160. contiguousPhysicalBytes = \
  161. fvdat->filesize - (off_t)(*bpnPtr * data->blocksize);
  162. /* contiguousPhysicalBytes cannot really be negative (could assert) */
  163. if (contiguousPhysicalBytes > (off_t)size) {
  164. contiguousPhysicalBytes = (off_t)size;
  165. }
  166. if (runPtr != NULL) {
  167. *runPtr = (size_t)contiguousPhysicalBytes;
  168. }
  169. if (poffPtr != NULL) {
  170. *poffPtr = 0;
  171. }
  172. return 0;
  173. }
  174. /*
  175. struct vnop_close_args {
  176. struct vnodeop_desc *a_desc;
  177. vnode_t a_vp;
  178. int a_fflag;
  179. vfs_context_t a_context;
  180. };
  181. */
  182. FUSE_VNOP_EXPORT
  183. int
  184. fuse_vnop_close(struct vnop_close_args *ap)
  185. {
  186. vnode_t vp = ap->a_vp;
  187. int fflag = ap->a_fflag;
  188. vfs_context_t context = ap->a_context;
  189. int err = 0;
  190. int isdir = (vnode_isdir(vp)) ? 1 : 0;
  191. fufh_type_t fufh_type;
  192. struct fuse_data *data;
  193. struct fuse_vnode_data *fvdat = VTOFUD(vp);
  194. struct fuse_filehandle *fufh = NULL;
  195. fuse_trace_printf_vnop();
  196. if (fuse_isdeadfs(vp)) {
  197. return 0;
  198. }
  199. /* vclean() calls VNOP_CLOSE with fflag set to IO_NDELAY. */
  200. if (fflag == IO_NDELAY) {
  201. return 0;
  202. }
  203. if (isdir) {
  204. fufh_type = FUFH_RDONLY;
  205. } else {
  206. fufh_type = fuse_filehandle_xlate_from_fflags(fflag);
  207. }
  208. fufh = &(fvdat->fufh[fufh_type]);
  209. if (!FUFH_IS_VALID(fufh)) {
  210. IOLog("MacFUSE: fufh invalid in close [type=%d oc=%d vtype=%d cf=%d]\n",
  211. fufh_type, fufh->open_count, vnode_vtype(vp), fflag);
  212. return 0;
  213. }
  214. if (isdir) {
  215. goto skipdir;
  216. }
  217. /*
  218. * Enforce sync-on-close unless explicitly told not to.
  219. *
  220. * We do this to maintain correct semantics in the not so common case when
  221. * you create a file with O_RDWR but without write permissions--you /are/
  222. * supposed to be able to write to such a file given the descriptor you
  223. * you got from open()/create(). Therefore, if we don't finish all our
  224. * writing before we close this precious writable descriptor, we might
  225. * be doomed.
  226. */
  227. if (vnode_hasdirtyblks(vp) && !fuse_isnosynconclose(vp)) {
  228. (void)cluster_push(vp, IO_SYNC | IO_CLOSE);
  229. }
  230. data = fuse_get_mpdata(vnode_mount(vp));
  231. if (fuse_implemented(data, FSESS_NOIMPLBIT(FLUSH))) {
  232. struct fuse_dispatcher fdi;
  233. struct fuse_flush_in *ffi;
  234. fdisp_init(&fdi, sizeof(*ffi));
  235. fdisp_make_vp(&fdi, FUSE_FLUSH, vp, context);
  236. ffi = fdi.indata;
  237. ffi->fh = fufh->fh_id;
  238. ffi->unused = 0;
  239. ffi->padding = 0;
  240. ffi->lock_owner = 0;
  241. err = fdisp_wait_answ(&fdi);
  242. if (!err) {
  243. fuse_ticket_drop(fdi.tick);
  244. } else {
  245. if (err == ENOSYS) {
  246. fuse_clear_implemented(data, FSESS_NOIMPLBIT(FLUSH));
  247. err = 0;
  248. }
  249. }
  250. }
  251. skipdir:
  252. /* This must be done after we have flushed any pending I/O. */
  253. FUFH_USE_DEC(fufh);
  254. if (!FUFH_IS_VALID(fufh)) {
  255. (void)fuse_filehandle_put(vp, context, fufh_type, FUSE_OP_FOREGROUNDED);
  256. }
  257. return err;
  258. }
  259. /*
  260. struct vnop_create_args {
  261. struct vnodeop_desc *a_desc;
  262. vnode_t a_dvp;
  263. vnode_t *a_vpp;
  264. struct componentname *a_cnp;
  265. struct vnode_attr *a_vap;
  266. vfs_context_t a_context;
  267. };
  268. */
  269. FUSE_VNOP_EXPORT
  270. int
  271. fuse_vnop_create(struct vnop_create_args *ap)
  272. {
  273. vnode_t dvp = ap->a_dvp;
  274. vnode_t *vpp = ap->a_vpp;
  275. struct componentname *cnp = ap->a_cnp;
  276. struct vnode_attr *vap = ap->a_vap;
  277. vfs_context_t context = ap->a_context;
  278. struct fuse_open_in *foi;
  279. struct fuse_mknod_in fmni;
  280. struct fuse_entry_out *feo;
  281. struct fuse_dispatcher fdi;
  282. struct fuse_dispatcher *fdip = &fdi;
  283. int err;
  284. int gone_good_old = 0;
  285. struct fuse_data *data = NULL;
  286. mount_t mp = vnode_mount(dvp);
  287. uint64_t parent_nodeid = VTOFUD(dvp)->nodeid;
  288. mode_t mode = MAKEIMODE(vap->va_type, vap->va_mode);
  289. fuse_trace_printf_vnop_novp();
  290. if (fuse_isdeadfs_fs(dvp)) {
  291. panic("MacFUSE: fuse_vnop_create(): called on a dead file system");
  292. }
  293. CHECK_BLANKET_DENIAL(dvp, context, EPERM);
  294. if (fuse_skip_apple_double_mp(mp, cnp->cn_nameptr, cnp->cn_namelen)) {
  295. return EPERM;
  296. }
  297. bzero(&fdi, sizeof(fdi));
  298. data = fuse_get_mpdata(mp);
  299. if (!fuse_implemented(data, FSESS_NOIMPLBIT(CREATE)) ||
  300. (vap->va_type != VREG)) {
  301. /* User-space file system does not implement CREATE */
  302. goto good_old;
  303. }
  304. fdisp_init(fdip, sizeof(*foi) + cnp->cn_namelen + 1);
  305. fdisp_make(fdip, FUSE_CREATE, vnode_mount(dvp), parent_nodeid, context);
  306. foi = fdip->indata;
  307. foi->mode = mode;
  308. /* XXX: We /always/ creat() like this. Wish we were on Linux. */
  309. foi->flags = O_CREAT | O_RDWR;
  310. memcpy((char *)fdip->indata + sizeof(*foi), cnp->cn_nameptr,
  311. cnp->cn_namelen);
  312. ((char *)fdip->indata)[sizeof(*foi) + cnp->cn_namelen] = '\0';
  313. err = fdisp_wait_answ(fdip);
  314. if (err == ENOSYS) {
  315. fuse_clear_implemented(data, FSESS_NOIMPLBIT(CREATE));
  316. fdip->tick = NULL;
  317. goto good_old;
  318. } else if (err) {
  319. goto undo;
  320. }
  321. goto bringup;
  322. good_old:
  323. gone_good_old = 1;
  324. fmni.mode = mode; /* fvdat->flags; */
  325. fmni.rdev = 0;
  326. fuse_internal_newentry_makerequest(vnode_mount(dvp), parent_nodeid, cnp,
  327. FUSE_MKNOD, &fmni, sizeof(fmni),
  328. fdip, context);
  329. err = fdisp_wait_answ(fdip);
  330. if (err) {
  331. goto undo;
  332. }
  333. bringup:
  334. feo = fdip->answ;
  335. if ((err = fuse_internal_checkentry(feo, VREG))) { // VBLK/VCHR not allowed
  336. fuse_ticket_drop(fdip->tick);
  337. goto undo;
  338. }
  339. err = FSNodeGetOrCreateFileVNodeByID(
  340. vpp, (gone_good_old) ? 0 : FN_CREATING,
  341. feo, mp, dvp, context, NULL /* oflags */);
  342. if (err) {
  343. if (gone_good_old) {
  344. fuse_internal_forget_send(mp, context, feo->nodeid, 1, fdip);
  345. } else {
  346. struct fuse_release_in *fri;
  347. uint64_t nodeid = feo->nodeid;
  348. uint64_t fh_id = ((struct fuse_open_out *)(feo + 1))->fh;
  349. fdisp_init(fdip, sizeof(*fri));
  350. fdisp_make(fdip, FUSE_RELEASE, mp, nodeid, context);
  351. fri = fdip->indata;
  352. fri->fh = fh_id;
  353. fri->flags = OFLAGS(mode);
  354. fuse_insert_callback(fdip->tick, fuse_internal_forget_callback);
  355. fuse_insert_message(fdip->tick);
  356. }
  357. return err;
  358. }
  359. fdip->answ = gone_good_old ? NULL : feo + 1;
  360. if (!gone_good_old) {
  361. uint64_t x_fh_id = ((struct fuse_open_out *)(feo + 1))->fh;
  362. uint32_t x_open_flags = ((struct fuse_open_out *)(feo + 1))->open_flags;
  363. struct fuse_vnode_data *fvdat = VTOFUD(*vpp);
  364. struct fuse_filehandle *fufh = &(fvdat->fufh[FUFH_RDWR]);
  365. fufh->fh_id = x_fh_id;
  366. fufh->open_flags = x_open_flags;
  367. /*
  368. * We're stashing this to be picked up by open. Meanwhile, we set
  369. * the use count to 1 because that's what it is. The use count will
  370. * later transfer to the slot that this handle ends up falling in.
  371. */
  372. fufh->open_count = 1;
  373. FUSE_OSAddAtomic(1, (SInt32 *)&fuse_fh_current);
  374. }
  375. cache_purge_negatives(dvp);
  376. fuse_ticket_drop(fdip->tick);
  377. FUSE_KNOTE(dvp, NOTE_WRITE);
  378. return 0;
  379. undo:
  380. return err;
  381. }
  382. /*
  383. struct vnop_exchange_args {
  384. struct vnodeop_desc *a_desc;
  385. vnode_t a_fvp;
  386. vnode_t a_tvp;
  387. int a_options;
  388. vfs_context_t a_context;
  389. };
  390. */
  391. FUSE_VNOP_EXPORT
  392. int
  393. fuse_vnop_exchange(struct vnop_exchange_args *ap)
  394. {
  395. #if M_MACFUSE_ENABLE_EXCHANGE
  396. vnode_t fvp = ap->a_fvp;
  397. vnode_t tvp = ap->a_tvp;
  398. int options = ap->a_options;
  399. vfs_context_t context = ap->a_context;
  400. const char *fname = NULL;
  401. const char *tname = NULL;
  402. size_t flen = 0;
  403. size_t tlen = 0;
  404. struct fuse_data *data = fuse_get_mpdata(vnode_mount(fvp));
  405. int err = 0;
  406. fuse_trace_printf_vnop_novp();
  407. if (vnode_mount(fvp) != vnode_mount(tvp)) {
  408. return EXDEV;
  409. }
  410. /* We now know f and t are on the same volume. */
  411. if (!fuse_implemented(data, FSESS_NOIMPLBIT(EXCHANGE))) {
  412. return ENOTSUP;
  413. }
  414. if (fuse_isnovncache(fvp)) {
  415. return ENOTSUP;
  416. }
  417. if (fvp == tvp) {
  418. return EINVAL;
  419. }
  420. if (!vnode_isreg(fvp) || !vnode_isreg(tvp)) {
  421. return EINVAL;
  422. }
  423. if (fuse_isdeadfs_fs(fvp)) {
  424. panic("MacFUSE: fuse_vnop_exchange(): called on a dead file system");
  425. }
  426. fname = vnode_getname(fvp);
  427. if (!fname) {
  428. return EIO;
  429. }
  430. tname = vnode_getname(tvp);
  431. if (!tname) {
  432. vnode_putname(fname);
  433. return EIO;
  434. }
  435. flen = strlen(fname);
  436. tlen = strlen(tname);
  437. if ((flen > 2) && (*fname == '.') && (*(fname + 1) == '_')) {
  438. err = EINVAL;
  439. goto out;
  440. }
  441. if ((tlen > 2) && (*tname == '.') && (*(tname + 1) == '_')) {
  442. err = EINVAL;
  443. goto out;
  444. }
  445. err = fuse_internal_exchange(fvp, fname, flen, tvp, tname, tlen, options,
  446. context);
  447. if (err == ENOSYS) {
  448. fuse_clear_implemented(data, FSESS_NOIMPLBIT(EXCHANGE));
  449. err = ENOTSUP;
  450. }
  451. out:
  452. vnode_putname(fname);
  453. vnode_putname(tname);
  454. if (err == 0) {
  455. FUSE_KNOTE(fvp, NOTE_ATTRIB);
  456. FUSE_KNOTE(tvp, NOTE_ATTRIB);
  457. }
  458. return err;
  459. #else /* !M_MACFUSE_ENABLE_EXCHANGE */
  460. (void)ap;
  461. return ENOTSUP;
  462. #endif /* M_MACFUSE_ENABLE_EXCHANGE */
  463. }
  464. /*
  465. * Our vnop_fsync roughly corresponds to the FUSE_FSYNC method. The Linux
  466. * version of FUSE also has a FUSE_FLUSH method.
  467. *
  468. * On Linux, fsync() synchronizes a file's complete in-core state with that
  469. * on disk. The call is not supposed to return until the system has completed
  470. * that action or until an error is detected.
  471. *
  472. * Linux also has an fdatasync() call that is similar to fsync() but is not
  473. * required to update the metadata such as access time and modification time.
  474. */
  475. /*
  476. struct vnop_fsync_args {
  477. struct vnodeop_desc *a_desc;
  478. vnode_t a_vp;
  479. int a_waitfor;
  480. vfs_context_t a_context;
  481. };
  482. */
  483. FUSE_VNOP_EXPORT
  484. int
  485. fuse_vnop_fsync(struct vnop_fsync_args *ap)
  486. {
  487. vnode_t vp = ap->a_vp;
  488. int waitfor = ap->a_waitfor;
  489. vfs_context_t context = ap->a_context;
  490. struct fuse_dispatcher fdi;
  491. struct fuse_filehandle *fufh;
  492. struct fuse_vnode_data *fvdat = VTOFUD(vp);
  493. int type, err = 0, tmp_err = 0;
  494. (void)waitfor;
  495. fuse_trace_printf_vnop();
  496. if (fuse_isdeadfs(vp)) {
  497. return 0;
  498. }
  499. cluster_push(vp, 0);
  500. /*
  501. * struct timeval tv;
  502. * int wait = (waitfor == MNT_WAIT)
  503. *
  504. * In another world, we could be doing something like:
  505. *
  506. * buf_flushdirtyblks(vp, wait, 0, (char *)"fuse_fsync");
  507. * microtime(&tv);
  508. * ...
  509. */
  510. /*
  511. * - UBC and vnode are in lock-step.
  512. * - Can call vnode_isinuse().
  513. * - Can call ubc_msync().
  514. */
  515. mount_t mp = vnode_mount(vp);
  516. if (!fuse_implemented(fuse_get_mpdata(mp), ((vnode_isdir(vp)) ?
  517. FSESS_NOIMPLBIT(FSYNCDIR) : FSESS_NOIMPLBIT(FSYNC)))) {
  518. err = ENOSYS;
  519. goto out;
  520. }
  521. fdisp_init(&fdi, 0);
  522. for (type = 0; type < FUFH_MAXTYPE; type++) {
  523. fufh = &(fvdat->fufh[type]);
  524. if (FUFH_IS_VALID(fufh)) {
  525. tmp_err = fuse_internal_fsync(vp, context, fufh, &fdi,
  526. FUSE_OP_FOREGROUNDED);
  527. if (tmp_err) {
  528. err = tmp_err;
  529. }
  530. }
  531. }
  532. out:
  533. if ((err == ENOSYS) && !fuse_isnosyncwrites_mp(mp)) {
  534. err = 0;
  535. }
  536. return err;
  537. }
  538. /*
  539. struct vnop_getattr_args {
  540. struct vnodeop_desc *a_desc;
  541. vnode_t a_vp;
  542. struct vnode_attr *a_vap;
  543. vfs_context_t a_context;
  544. };
  545. */
  546. FUSE_VNOP_EXPORT
  547. int
  548. fuse_vnop_getattr(struct vnop_getattr_args *ap)
  549. {
  550. vnode_t vp = ap->a_vp;
  551. struct vnode_attr *vap = ap->a_vap;
  552. vfs_context_t context = ap->a_context;
  553. int err = 0;
  554. int dataflags;
  555. struct timespec uptsp;
  556. struct fuse_dispatcher fdi;
  557. struct fuse_data *data;
  558. data = fuse_get_mpdata(vnode_mount(vp));
  559. fuse_trace_printf_vnop();
  560. if (fuse_isdeadfs(vp)) {
  561. if (vnode_isvroot(vp)) {
  562. goto fake;
  563. } else {
  564. return EBADF;
  565. }
  566. }
  567. if (!vnode_isvroot(vp) || !fuse_vfs_context_issuser(context)) {
  568. CHECK_BLANKET_DENIAL(vp, context, ENOENT);
  569. }
  570. dataflags = data->dataflags;
  571. /* Note that we are not bailing out on a dead file system just yet. */
  572. /* look for cached attributes */
  573. nanouptime(&uptsp);
  574. if (fuse_timespec_cmp(&uptsp, &VTOFUD(vp)->attr_valid, <=)) {
  575. if (vap != VTOVA(vp)) {
  576. fuse_internal_attr_loadvap(vp, vap, context);
  577. }
  578. return 0;
  579. }
  580. if (!(dataflags & FSESS_INITED)) {
  581. if (!vnode_isvroot(vp)) {
  582. fdata_set_dead(data);
  583. err = ENOTCONN;
  584. return err;
  585. } else {
  586. goto fake;
  587. }
  588. }
  589. if ((err = fdisp_simple_putget_vp(&fdi, FUSE_GETATTR, vp, context))) {
  590. if ((err == ENOTCONN) && vnode_isvroot(vp)) {
  591. /* see comment at similar place in fuse_statfs() */
  592. goto fake;
  593. }
  594. if (err == ENOENT) {
  595. fuse_internal_vnode_disappear(vp, context, REVOKE_SOFT);
  596. }
  597. return err;
  598. }
  599. /* XXX: Could check the sanity/volatility of va_mode here. */
  600. if ((((struct fuse_attr_out *)fdi.answ)->attr.mode & S_IFMT) == 0) {
  601. return EIO;
  602. }
  603. cache_attrs(vp, (struct fuse_attr_out *)fdi.answ);
  604. VTOFUD(vp)->c_flag &= ~C_XTIMES_VALID;
  605. fuse_internal_attr_loadvap(vp, vap, context);
  606. #if M_MACFUSE_EXPERIMENTAL_JUNK
  607. if (vap != VTOVA(vp)) {
  608. memcpy(vap, VTOVA(vp), sizeof(*vap));
  609. }
  610. #endif
  611. /* ATTR_FUDGE_CASE */
  612. if (vnode_isreg(vp) && fuse_isnoubc(vp)) {
  613. /*
  614. * This is for those cases when the file size changed without us
  615. * knowing, and we want to catch up.
  616. *
  617. * For the sake of sanity, we don't want to do it with UBC.
  618. * We also don't want to do it when we have asynchronous writes
  619. * enabled because we might have pending writes on *our* side.
  620. * We're not researching distributed file systems here!
  621. *
  622. */
  623. struct fuse_vnode_data *fvdat = VTOFUD(vp);
  624. off_t new_filesize = ((struct fuse_attr_out *)fdi.answ)->attr.size;
  625. fvdat->filesize = new_filesize;
  626. }
  627. fuse_ticket_drop(fdi.tick);
  628. if (vnode_vtype(vp) != vap->va_type) {
  629. if ((vnode_vtype(vp) == VNON) && (vap->va_type != VNON)) {
  630. /*
  631. * We should be doing the following:
  632. *
  633. * vp->vtype = vap->v_type
  634. */
  635. } else {
  636. /*
  637. * STALE vnode, ditch
  638. *
  639. * The vnode has changed its type "behind our back". There's
  640. * nothing really we can do, so let us just force an internal
  641. * revocation.
  642. */
  643. fuse_internal_vnode_disappear(vp, context, REVOKE_SOFT);
  644. return EIO;
  645. }
  646. }
  647. return 0;
  648. fake:
  649. bzero(vap, sizeof(*vap));
  650. VATTR_RETURN(vap, va_type, vnode_vtype(vp));
  651. VATTR_RETURN(vap, va_uid, data->daemoncred->cr_uid);
  652. VATTR_RETURN(vap, va_gid, data->daemoncred->cr_gid);
  653. VATTR_RETURN(vap, va_mode, S_IRWXU);
  654. return 0;
  655. }
  656. #if M_MACFUSE_ENABLE_XATTR
  657. /*
  658. struct vnop_getxattr_args {
  659. struct vnodeop_desc *a_desc;
  660. vnode_t a_vp;
  661. char *a_name;
  662. uio_t a_uio;
  663. size_t *a_size;
  664. int a_options;
  665. vfs_context_t a_context;
  666. };
  667. */
  668. FUSE_VNOP_EXPORT
  669. int
  670. fuse_vnop_getxattr(struct vnop_getxattr_args *ap)
  671. {
  672. vnode_t vp = ap->a_vp;
  673. const char *name = ap->a_name;
  674. uio_t uio = ap->a_uio;
  675. vfs_context_t context = ap->a_context;
  676. struct fuse_dispatcher fdi;
  677. struct fuse_getxattr_in *fgxi;
  678. struct fuse_getxattr_out *fgxo;
  679. struct fuse_data *data;
  680. mount_t mp;
  681. int err = 0;
  682. size_t namelen;
  683. fuse_trace_printf_vnop();
  684. if (fuse_isdeadfs(vp)) {
  685. return EBADF;
  686. }
  687. CHECK_BLANKET_DENIAL(vp, context, ENOENT);
  688. if (name == NULL || name[0] == '\0') {
  689. return EINVAL;
  690. }
  691. mp = vnode_mount(vp);
  692. data = fuse_get_mpdata(mp);
  693. if (fuse_skip_apple_xattr_mp(mp, name)) {
  694. return EPERM;
  695. }
  696. if (data->dataflags & FSESS_AUTO_XATTR) {
  697. return ENOTSUP;
  698. }
  699. if (!fuse_implemented(data, FSESS_NOIMPLBIT(GETXATTR))) {
  700. return ENOTSUP;
  701. }
  702. namelen = strlen(name);
  703. fdisp_init(&fdi, sizeof(*fgxi) + namelen + 1);
  704. fdisp_make_vp(&fdi, FUSE_GETXATTR, vp, context);
  705. fgxi = fdi.indata;
  706. if (uio) {
  707. fgxi->size = (uint32_t)uio_resid(uio);
  708. } else {
  709. fgxi->size = 0;
  710. }
  711. fgxi->position = (uint32_t)uio_offset(uio);
  712. memcpy((char *)fdi.indata + sizeof(*fgxi), name, namelen);
  713. ((char *)fdi.indata)[sizeof(*fgxi) + namelen] = '\0';
  714. if (fgxi->size > FUSE_REASONABLE_XATTRSIZE) {
  715. fticket_set_killl(fdi.tick);
  716. }
  717. err = fdisp_wait_answ(&fdi);
  718. if (err) {
  719. if (err == ENOSYS) {
  720. fuse_clear_implemented(data, FSESS_NOIMPLBIT(GETXATTR));
  721. return ENOTSUP;
  722. }
  723. return err;
  724. }
  725. if (uio) {
  726. *ap->a_size = fdi.iosize;
  727. if ((user_ssize_t)fdi.iosize > uio_resid(uio)) {
  728. err = ERANGE;
  729. } else {
  730. err = uiomove((char *)fdi.answ, (int)fdi.iosize, uio);
  731. }
  732. } else {
  733. fgxo = (struct fuse_getxattr_out *)fdi.answ;
  734. *ap->a_size = fgxo->size;
  735. }
  736. fuse_ticket_drop(fdi.tick);
  737. return err;
  738. }
  739. #endif /* M_MACFUSE_ENABLE_XATTR */
  740. /*
  741. struct vnop_inactive_args {
  742. struct vnodeop_desc *a_desc;
  743. vnode_t a_vp;
  744. vfs_context_t a_context;
  745. };
  746. */
  747. FUSE_VNOP_EXPORT
  748. int
  749. fuse_vnop_inactive(struct vnop_inactive_args *ap)
  750. {
  751. vnode_t vp = ap->a_vp;
  752. vfs_context_t context = ap->a_context;
  753. struct fuse_vnode_data *fvdat = VTOFUD(vp);
  754. struct fuse_filehandle *fufh = NULL;
  755. int fufh_type;
  756. fuse_trace_printf_vnop();
  757. /*
  758. * Cannot do early bail out on a dead file system in this case.
  759. */
  760. for (fufh_type = 0; fufh_type < FUFH_MAXTYPE; fufh_type++) {
  761. fufh = &(fvdat->fufh[fufh_type]);
  762. if (FUFH_IS_VALID(fufh)) {
  763. FUFH_USE_RESET(fufh);
  764. (void)fuse_filehandle_put(vp, context, fufh_type,
  765. FUSE_OP_FOREGROUNDED);
  766. }
  767. }
  768. return 0;
  769. }
  770. extern int fuse_setextendedsecurity(mount_t mp, int state);
  771. /*
  772. struct vnop_ioctl_args {
  773. struct vnodeop_desc *a_desc;
  774. vnode_t a_vp;
  775. u_long a_command;
  776. caddr_t a_data;
  777. int a_fflag;
  778. vfs_context_t a_context;
  779. };
  780. */
  781. FUSE_VNOP_EXPORT
  782. int
  783. fuse_vnop_ioctl(struct vnop_ioctl_args *ap)
  784. {
  785. vnode_t vp = ap->a_vp;
  786. u_long cmd = ap->a_command;
  787. vfs_context_t context = ap->a_context;
  788. int ret = EINVAL;
  789. fuse_trace_printf_vnop();
  790. if (fuse_isdeadfs(vp)) {
  791. return EBADF;
  792. }
  793. switch (cmd) {
  794. case FSCTLSETACLSTATE:
  795. {
  796. int state;
  797. mount_t mp;
  798. struct fuse_data *data;
  799. if (ap->a_data == NULL) {
  800. return EINVAL;
  801. }
  802. mp = vnode_mount(vp);
  803. data = fuse_get_mpdata(mp);
  804. if (!fuse_vfs_context_issuser(context) &&
  805. !(fuse_match_cred(data->daemoncred,
  806. vfs_context_ucred(context)))) {
  807. return EPERM;
  808. }
  809. state = *(int *)ap->a_data;
  810. return fuse_setextendedsecurity(mp, state);
  811. }
  812. break;
  813. case FSCTLALTERVNODEFORINODE:
  814. /*
  815. * This is the fsctl() version of the AVFI device ioctl's in
  816. * fuse_device.c. Since the device ioctl's must be used from
  817. * within the file system (we don't allow multiple device opens),
  818. * it's rather painful to test/experiment with them. The fsctl
  819. * version is easier to use. To simplify things, the "path" in
  820. * the fsctl() call /must/ be the root of the file system.
  821. */
  822. if (!vnode_isvroot(vp)) {
  823. return EINVAL;
  824. }
  825. ret = fuse_internal_ioctl_avfi(vp, context,
  826. (struct fuse_avfi_ioctl *)(ap->a_data));
  827. break;
  828. default:
  829. break;
  830. }
  831. return ret;
  832. }
  833. #if M_MACFUSE_ENABLE_KQUEUE
  834. #include "fuse_knote.h"
  835. /*
  836. struct vnop_kqfilt_add_args {
  837. struct vnodeop_desc *a_desc;
  838. vnode_t a_vp;
  839. struct knote *a_kn;
  840. struct proc *p;
  841. vfs_context_t a_context;
  842. };
  843. */
  844. FUSE_VNOP_EXPORT
  845. int
  846. fuse_vnop_kqfilt_add(struct vnop_kqfilt_add_args *ap)
  847. {
  848. vnode_t vp = ap->a_vp;
  849. struct knote *kn = ap->a_kn;
  850. fuse_trace_printf_vnop();
  851. if (fuse_isdeadfs(vp)) {
  852. return EBADF;
  853. }
  854. switch (kn->kn_filter) {
  855. case EVFILT_READ:
  856. if (vnode_isreg(vp)) {
  857. kn->kn_fop = &fuseread_filtops;
  858. } else {
  859. return EINVAL;
  860. }
  861. break;
  862. case EVFILT_WRITE:
  863. if (vnode_isreg(vp)) {
  864. kn->kn_fop = &fusewrite_filtops;
  865. } else {
  866. return EINVAL;
  867. }
  868. break;
  869. case EVFILT_VNODE:
  870. kn->kn_fop = &fusevnode_filtops;
  871. break;
  872. default:
  873. return 1;
  874. }
  875. kn->kn_hook = (caddr_t)vp;
  876. kn->kn_hookid = vnode_vid(vp);
  877. /* lock */
  878. KNOTE_ATTACH(&VTOFUD(vp)->c_knotes, kn);
  879. /* unlock */
  880. return 0;
  881. }
  882. /*
  883. struct vnop_kqfilt_remove_args {
  884. struct vnodeop_desc *a_desc;
  885. vnode_t a_vp;
  886. uintptr_t ident;
  887. vfs_context_t a_context;
  888. };
  889. */
  890. FUSE_VNOP_EXPORT
  891. int
  892. fuse_vnop_kqfilt_remove(__unused struct vnop_kqfilt_remove_args *ap)
  893. {
  894. fuse_trace_printf_vnop_novp();
  895. return ENOTSUP;
  896. }
  897. #endif /* M_MACFUSE_ENABLE_KQUEUE */
  898. /*
  899. struct vnop_link_args {
  900. struct vnodeop_desc *a_desc;
  901. vnode_t a_vp;
  902. vnode_t a_tdvp;
  903. struct componentname *a_cnp;
  904. vfs_context_t a_context;
  905. };
  906. */
  907. FUSE_VNOP_EXPORT
  908. int
  909. fuse_vnop_link(struct vnop_link_args *ap)
  910. {
  911. vnode_t vp = ap->a_vp;
  912. vnode_t tdvp = ap->a_tdvp;
  913. struct componentname *cnp = ap->a_cnp;
  914. vfs_context_t context = ap->a_context;
  915. struct vnode_attr *vap = VTOVA(vp);
  916. struct fuse_dispatcher fdi;
  917. struct fuse_entry_out *feo;
  918. struct fuse_link_in fli;
  919. int err;
  920. fuse_trace_printf_vnop();
  921. if (fuse_isdeadfs_fs(vp)) {
  922. panic("MacFUSE: fuse_vnop_link(): called on a dead file system");
  923. }
  924. if (vnode_mount(tdvp) != vnode_mount(vp)) {
  925. return EXDEV;
  926. }
  927. if (vap->va_nlink >= FUSE_LINK_MAX) {
  928. return EMLINK;
  929. }
  930. CHECK_BLANKET_DENIAL(vp, context, EPERM);
  931. fli.oldnodeid = VTOI(vp);
  932. fdisp_init(&fdi, 0);
  933. fuse_internal_newentry_makerequest(vnode_mount(tdvp), VTOI(tdvp), cnp,
  934. FUSE_LINK, &fli, sizeof(fli), &fdi,
  935. context);
  936. if ((err = fdisp_wait_answ(&fdi))) {
  937. return err;
  938. }
  939. feo = fdi.answ;
  940. err = fuse_internal_checkentry(feo, vnode_vtype(vp));
  941. fuse_ticket_drop(fdi.tick);
  942. fuse_invalidate_attr(tdvp);
  943. fuse_invalidate_attr(vp);
  944. if (err == 0) {
  945. FUSE_KNOTE(vp, NOTE_LINK);
  946. FUSE_KNOTE(tdvp, NOTE_WRITE);
  947. VTOFUD(vp)->nlookup++;
  948. }
  949. return err;
  950. }
  951. #if M_MACFUSE_ENABLE_XATTR
  952. /*
  953. struct vnop_listxattr_args {
  954. struct vnodeop_desc *a_desc;
  955. vnode_t a_vp;
  956. uio_t a_uio;
  957. size_t *a_size;
  958. int a_options;
  959. vfs_context_t a_context;
  960. };
  961. */
  962. FUSE_VNOP_EXPORT
  963. int
  964. fuse_vnop_listxattr(struct vnop_listxattr_args *ap)
  965. {
  966. vnode_t vp = ap->a_vp;
  967. uio_t uio = ap->a_uio;
  968. vfs_context_t context = ap->a_context;
  969. struct fuse_dispatcher fdi;
  970. struct fuse_getxattr_in *fgxi;
  971. struct fuse_getxattr_out *fgxo;
  972. struct fuse_data *data;
  973. int err = 0;
  974. fuse_trace_printf_vnop();
  975. if (fuse_isdeadfs(vp)) {
  976. return EBADF;
  977. }
  978. CHECK_BLANKET_DENIAL(vp, context, ENOENT);
  979. data = fuse_get_mpdata(vnode_mount(vp));
  980. if (data->dataflags & FSESS_AUTO_XATTR) {
  981. return ENOTSUP;
  982. }
  983. if (!fuse_implemented(data, FSESS_NOIMPLBIT(LISTXATTR))) {
  984. return ENOTSUP;
  985. }
  986. fdisp_init(&fdi, sizeof(*fgxi));
  987. fdisp_make_vp(&fdi, FUSE_LISTXATTR, vp, context);
  988. fgxi = fdi.indata;
  989. if (uio) {
  990. fgxi->size = (uint32_t)uio_resid(uio);
  991. } else {
  992. fgxi->size = 0;
  993. }
  994. err = fdisp_wait_answ(&fdi);
  995. if (err) {
  996. if (err == ENOSYS) {
  997. fuse_clear_implemented(data, FSESS_NOIMPLBIT(LISTXATTR));
  998. return ENOTSUP;
  999. }
  1000. return err;
  1001. }
  1002. if (uio) {
  1003. *ap->a_size = fdi.iosize;
  1004. if ((user_ssize_t)fdi.iosize > uio_resid(uio)) {
  1005. err = ERANGE;
  1006. } else {
  1007. err = uiomove((char *)fdi.answ, (int)fdi.iosize, uio);
  1008. }
  1009. } else {
  1010. fgxo = (struct fuse_getxattr_out *)fdi.answ;
  1011. *ap->a_size = fgxo->size;
  1012. }
  1013. fuse_ticket_drop(fdi.tick);
  1014. return err;
  1015. }
  1016. #endif /* M_MACFUSE_ENABLE_XATTR */
  1017. /*
  1018. struct vnop_lookup_args {
  1019. struct vnodeop_desc *a_desc;
  1020. vnode_t a_dvp;
  1021. vnode_t *a_vpp;
  1022. struct componentname *a_cnp;
  1023. vfs_context_t a_context;
  1024. };
  1025. */
  1026. FUSE_VNOP_EXPORT
  1027. int
  1028. fuse_vnop_lookup(struct vnop_lookup_args *ap)
  1029. {
  1030. vnode_t dvp = ap->a_dvp;
  1031. vnode_t *vpp = ap->a_vpp;
  1032. struct componentname *cnp = ap->a_cnp;
  1033. vfs_context_t context = ap->a_context;
  1034. int nameiop = cnp->cn_nameiop;
  1035. int flags = cnp->cn_flags;
  1036. int wantparent = flags & (LOCKPARENT|WANTPARENT);
  1037. int islastcn = flags & ISLASTCN;
  1038. int isdot = FALSE;
  1039. int isdotdot = FALSE;
  1040. mount_t mp = vnode_mount(dvp);
  1041. int err = 0;
  1042. int lookup_err = 0;
  1043. vnode_t vp = NULL;
  1044. vnode_t pdp = (vnode_t)NULL;
  1045. uint64_t size = FUSE_ZERO_SIZE;
  1046. struct fuse_dispatcher fdi;
  1047. enum fuse_opcode op;
  1048. uint64_t nodeid;
  1049. uint64_t parent_nodeid;
  1050. *vpp = NULLVP;
  1051. fuse_trace_printf_vnop_novp();
  1052. if (fuse_isdeadfs(dvp)) {
  1053. *ap->a_vpp = NULLVP;
  1054. return ENOTDIR;
  1055. }
  1056. if (fuse_skip_apple_double_mp(mp, cnp->cn_nameptr, cnp->cn_namelen)) {
  1057. return ENOENT;
  1058. }
  1059. if (!vnode_isdir(dvp)) {
  1060. return ENOTDIR;
  1061. }
  1062. if (islastcn && vfs_isrdonly(mp) && (nameiop != LOOKUP)) {
  1063. return EROFS;
  1064. }
  1065. if (cnp->cn_namelen > FUSE_MAXNAMLEN) {
  1066. return ENAMETOOLONG;
  1067. }
  1068. if (flags & ISDOTDOT) {
  1069. isdotdot = TRUE;
  1070. } else if ((cnp->cn_nameptr[0] == '.') && (cnp->cn_namelen == 1)) {
  1071. isdot = TRUE;
  1072. }
  1073. if (isdotdot) {
  1074. pdp = VTOFUD(dvp)->parentvp;
  1075. nodeid = VTOI(pdp);
  1076. parent_nodeid = VTOFUD(dvp)->parent_nodeid;
  1077. fdisp_init(&fdi, 0);
  1078. op = FUSE_GETATTR;
  1079. goto calldaemon;
  1080. } else if (isdot) {
  1081. nodeid = VTOI(dvp);
  1082. parent_nodeid = VTOFUD(dvp)->parent_nodeid;
  1083. fdisp_init(&fdi, 0);
  1084. op = FUSE_GETATTR;
  1085. goto calldaemon;
  1086. } else {
  1087. err = fuse_vncache_lookup(dvp, vpp, cnp);
  1088. switch (err) {
  1089. case -1: /* positive match */
  1090. if (fuse_isnovncache(*vpp)) {
  1091. fuse_vncache_purge(*vpp);
  1092. vnode_put(*vpp);
  1093. *vpp = NULL;
  1094. FUSE_OSAddAtomic(1, (SInt32 *)&fuse_lookup_cache_overrides);
  1095. err = 0;
  1096. break; /* pretend it's a miss */
  1097. }
  1098. FUSE_OSAddAtomic(1, (SInt32 *)&fuse_lookup_cache_hits);
  1099. return 0;
  1100. case 0: /* no match in cache (or aged out) */
  1101. FUSE_OSAddAtomic(1, (SInt32 *)&fuse_lookup_cache_misses);
  1102. break;
  1103. case ENOENT: /* negative match */
  1104. /* fall through */
  1105. default:
  1106. return err;
  1107. }
  1108. }
  1109. nodeid = VTOI(dvp);
  1110. parent_nodeid = VTOI(dvp);
  1111. fdisp_init(&fdi, cnp->cn_namelen + 1);
  1112. op = FUSE_LOOKUP;
  1113. calldaemon:
  1114. fdisp_make(&fdi, op, mp, nodeid, context);
  1115. if (op == FUSE_LOOKUP) {
  1116. memcpy(fdi.indata, cnp->cn_nameptr, cnp->cn_namelen);
  1117. ((char *)fdi.indata)[cnp->cn_namelen] = '\0';
  1118. }
  1119. lookup_err = fdisp_wait_answ(&fdi);
  1120. if ((op == FUSE_LOOKUP) && !lookup_err) { /* lookup call succeeded */
  1121. nodeid = ((struct fuse_entry_out *)fdi.answ)->nodeid;
  1122. size = ((struct fuse_entry_out *)fdi.answ)->attr.size;
  1123. if (!nodeid) {
  1124. fdi.answ_stat = ENOENT; /* XXX: negative_timeout case */
  1125. lookup_err = ENOENT;
  1126. } else if (nodeid == FUSE_ROOT_ID) {
  1127. lookup_err = EINVAL;
  1128. }
  1129. }
  1130. /*
  1131. * If we get (lookup_err != 0), that means we didn't find what we were
  1132. * looking for. This can still be OK if we're creating or renaming and
  1133. * are at the end of the pathname.
  1134. */
  1135. if (lookup_err &&
  1136. (!fdi.answ_stat || lookup_err != ENOENT || op != FUSE_LOOKUP)) {
  1137. return lookup_err;
  1138. }
  1139. /* lookup_err, if non-zero, must be ENOENT at this point */
  1140. if (lookup_err) {
  1141. if ((nameiop == CREATE || nameiop == RENAME) && islastcn
  1142. /* && directory dvp has not been removed */) {
  1143. /*
  1144. * EROFS case has already been covered.
  1145. *
  1146. * if (vfs_isrdonly(mp)) {
  1147. * err = EROFS;
  1148. * goto out;
  1149. * }
  1150. */
  1151. err = EJUSTRETURN;
  1152. goto out;
  1153. }
  1154. if (fuse_isnegativevncache_mp(mp)) {
  1155. if ((cnp->cn_flags & MAKEENTRY) && (nameiop != CREATE)) {
  1156. fuse_vncache_enter(dvp, NULLVP, cnp);
  1157. }
  1158. }
  1159. err = ENOENT;
  1160. goto out;
  1161. } else {
  1162. /* !lookup_err */
  1163. struct fuse_entry_out *feo = NULL;
  1164. struct fuse_attr *fattr = NULL;
  1165. if (op == FUSE_GETATTR) {
  1166. fattr = &((struct fuse_attr_out *)fdi.answ)->attr;
  1167. } else {
  1168. feo = (struct fuse_entry_out *)fdi.answ;
  1169. fattr = &(feo->attr);
  1170. }
  1171. /* Sanity check(s) */
  1172. if ((fattr->mode & S_IFMT) == 0) {
  1173. err = EIO;
  1174. goto out;
  1175. }
  1176. if ((nameiop == DELETE) && islastcn) {
  1177. if (isdot) {
  1178. err = vnode_get(dvp);
  1179. if (err == 0) {
  1180. *vpp = dvp;
  1181. }
  1182. goto out;
  1183. }
  1184. if ((err = fuse_vget_i(&vp, 0 /* flags */, feo, cnp, dvp,
  1185. mp, context))) {
  1186. goto out;
  1187. }
  1188. *vpp = vp;
  1189. goto out;
  1190. }
  1191. if ((nameiop == RENAME) && islastcn && wantparent) {
  1192. if (isdot) {
  1193. err = EISDIR;
  1194. goto out;
  1195. }
  1196. if ((err = fuse_vget_i(&vp, 0 /* flags */, feo, cnp, dvp,
  1197. mp, context))) {
  1198. goto out;
  1199. }
  1200. *vpp = vp;
  1201. goto out;
  1202. }
  1203. if (isdotdot) {
  1204. err = vnode_get(pdp);
  1205. if (err == 0) {
  1206. *vpp = pdp;
  1207. }
  1208. } else if (isdot) { /* nodeid == VTOI(dvp) */
  1209. err = vnode_get(dvp);
  1210. if (err == 0) {
  1211. *vpp = dvp;
  1212. }
  1213. } else {
  1214. if ((err = fuse_vget_i(&vp, 0 /* flags */, feo, cnp, dvp,
  1215. mp, context))) {
  1216. goto out;
  1217. }
  1218. *vpp = vp;
  1219. }
  1220. if (op == FUSE_GETATTR) {
  1221. /* ATTR_FUDGE_CASE */
  1222. if (vnode_isreg(*vpp) && fuse_isnoubc(vp)) {
  1223. VTOFUD(*vpp)->filesize =
  1224. ((struct fuse_attr_out *)fdi.answ)->attr.size;
  1225. }
  1226. cache_attrs(*vpp, (struct fuse_attr_out *)fdi.answ);
  1227. } else {
  1228. /* ATTR_FUDGE_CASE */
  1229. if (vnode_isreg(*vpp) && fuse_isnoubc(vp)) {
  1230. VTOFUD(*vpp)->filesize =
  1231. ((struct fuse_entry_out *)fdi.answ)->attr.size;
  1232. }
  1233. cache_attrs(*vpp, (struct fuse_entry_out *)fdi.answ);
  1234. }
  1235. /*
  1236. * We do this elsewhere...
  1237. *
  1238. * if (cnp->cn_flags & MAKEENTRY) {
  1239. * fuse_vncache_enter(dvp, *vpp, cnp);
  1240. * }
  1241. */
  1242. }
  1243. out:
  1244. if (!lookup_err) {
  1245. /* No lookup error; need to clean up. */
  1246. if (err) { /* Found inode; exit with no vnode. */
  1247. if (op == FUSE_LOOKUP) {
  1248. fuse_internal_forget_send(vnode_mount(dvp), context,
  1249. nodeid, 1, &fdi);
  1250. }
  1251. return err;
  1252. } else {
  1253. if (!islastcn) {
  1254. int tmpvtype = vnode_vtype(*vpp);
  1255. if ((tmpvtype != VDIR) && (tmpvtype != VLNK)) {
  1256. err = ENOTDIR;
  1257. }
  1258. /* if (!err && !vnode_mountedhere(*vpp)) { ... */
  1259. if (err) {
  1260. vnode_put(*vpp);
  1261. *vpp = NULL;
  1262. }
  1263. }
  1264. }
  1265. fuse_ticket_drop(fdi.tick);
  1266. }
  1267. return err;
  1268. }
  1269. /*
  1270. struct vnop_mkdir_args {
  1271. struct vnodeop_desc *a_desc;
  1272. vnode_t a_dvp;
  1273. vnode_t *a_vpp;
  1274. struct componentname *a_cnp;
  1275. struct vnode_attr *a_vap;
  1276. vfs_context_t a_context;
  1277. };
  1278. */
  1279. FUSE_VNOP_EXPORT
  1280. int
  1281. fuse_vnop_mkdir(struct vnop_mkdir_args *ap)
  1282. {
  1283. vnode_t dvp = ap->a_dvp;
  1284. vnode_t *vpp = ap->a_vpp;
  1285. struct componentname *cnp = ap->a_cnp;
  1286. struct vnode_attr *vap = ap->a_vap;
  1287. vfs_context_t context = ap->a_context;
  1288. int err = 0;
  1289. struct fuse_mkdir_in fmdi;
  1290. fuse_trace_printf_vnop_novp();
  1291. if (fuse_isdeadfs_fs(dvp)) {
  1292. panic("MacFUSE: fuse_vnop_mkdir(): called on a dead file system");
  1293. }
  1294. CHECK_BLANKET_DENIAL(dvp, context, EPERM);
  1295. fmdi.mode = MAKEIMODE(vap->va_type, vap->va_mode);
  1296. err = fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKDIR, &fmdi,
  1297. sizeof(fmdi), VDIR, context);
  1298. if (err == 0) {
  1299. fuse_invalidate_attr(dvp);
  1300. FUSE_KNOTE(dvp, NOTE_WRITE | NOTE_LINK);
  1301. }
  1302. return err;
  1303. }
  1304. /*
  1305. struct vnop_mknod_args {
  1306. struct vnodeop_desc *a_desc;
  1307. vnode_t a_dvp;
  1308. vnode_t *a_vpp;
  1309. struct componentname *a_cnp;
  1310. struct vnode_attr *a_vap;
  1311. vfs_context_t a_context;
  1312. };
  1313. */
  1314. FUSE_VNOP_EXPORT
  1315. int
  1316. fuse_vnop_mknod(struct vnop_mknod_args *ap)
  1317. {
  1318. vnode_t dvp = ap->a_dvp;
  1319. vnode_t *vpp = ap->a_vpp;
  1320. struct componentname *cnp = ap->a_cnp;
  1321. struct vnode_attr *vap = ap->a_vap;
  1322. vfs_context_t context = ap->a_context;
  1323. struct fuse_mknod_in fmni;
  1324. int err;
  1325. fuse_trace_printf_vnop_novp();
  1326. if (fuse_isdeadfs_fs(dvp)) {
  1327. panic("MacFUSE: fuse_vnop_mknod(): called on a dead file system");
  1328. }
  1329. CHECK_BLANKET_DENIAL(dvp, context, EPERM);
  1330. fmni.mode = MAKEIMODE(vap->va_type, vap->va_mode);
  1331. fmni.rdev = vap->va_rdev;
  1332. err = fuse_internal_newentry(dvp, vpp, cnp, FUSE_MKNOD, &fmni,
  1333. sizeof(fmni), vap->va_type, context);
  1334. if (err== 0) {
  1335. fuse_invalidate_attr(dvp);
  1336. FUSE_KNOTE(dvp, NOTE_WRITE);
  1337. }
  1338. return err;
  1339. }
  1340. /*
  1341. struct vnop_mmap_args {
  1342. struct vnodeop_desc *a_desc;
  1343. vnode_t a_vp;
  1344. int a_fflags;
  1345. vfs_context_t a_context;
  1346. };
  1347. */
  1348. FUSE_VNOP_EXPORT
  1349. int
  1350. fuse_vnop_mmap(struct vnop_mmap_args *ap)
  1351. {
  1352. vnode_t vp = ap->a_vp;
  1353. int fflags = ap->a_fflags;
  1354. vfs_context_t context = ap->a_context;
  1355. struct fuse_vnode_data *fvdat = VTOFUD(vp);
  1356. struct fuse_filehandle *fufh = NULL;
  1357. fufh_type_t fufh_type = fuse_filehandle_xlate_from_mmap(fflags);
  1358. int err = 0;
  1359. int deleted = 0;
  1360. int retried = 0;
  1361. fuse_trace_printf_vnop();
  1362. if (fuse_isdeadfs_fs(vp)) {
  1363. panic("MacFUSE: fuse_vnop_mmap(): called on a dead file system");
  1364. }
  1365. if (fuse_isdirectio(vp)) {
  1366. /*
  1367. * We should be returning ENODEV here, but ubc_map() translates
  1368. * all errors except ENOPERM to 0. Even then, this is not going
  1369. * to prevent the mmap()!
  1370. */
  1371. return EPERM;
  1372. }
  1373. CHECK_BLANKET_DENIAL(vp, context, ENOENT);
  1374. if (fufh_type == FUFH_INVALID) { /* nothing to do */
  1375. return 0;
  1376. }
  1377. /* XXX: For PROT_WRITE, we should only care if file is mapped MAP_SHARED. */
  1378. retry:
  1379. fufh = &(fvdat->fufh[fufh_type]);
  1380. if (FUFH_IS_VALID(fufh)) {
  1381. FUFH_USE_INC(fufh);
  1382. FUSE_OSAddAtomic(1, (SInt32 *)&fuse_fh_reuse_count);
  1383. goto out;
  1384. }
  1385. if (!deleted) {
  1386. err = fuse_filehandle_preflight_status(vp, fvdat->parentvp,
  1387. context, fufh_type);
  1388. if (err == ENOENT) {
  1389. deleted = 1;
  1390. err = 0;
  1391. }
  1392. }
  1393. #if FUSE_DEBUG
  1394. fuse_preflight_log(vp, fufh_type, err, "mmap");
  1395. #endif /* FUSE_DEBUG */
  1396. if (!err) {
  1397. err = fuse_filehandle_get(vp, context, fufh_type, 0 /* mode */);
  1398. }
  1399. if (err) {
  1400. /*
  1401. * XXX: This is a kludge because xnu doesn't tell us whether this
  1402. * is a MAP_SHARED or MAP_PRIVATE mapping. If we want shared
  1403. * library mapping to go well, we need to do this.
  1404. */
  1405. if (!retried && (err == EACCES) &&
  1406. ((fufh_type == FUFH_RDWR) || (fufh_type == FUFH_WRONLY))) {
  1407. IOLog("MacFUSE: filehandle_get retrying (type=%d, err=%d)\n",
  1408. fufh_type, err);
  1409. fufh_type = FUFH_RDONLY;
  1410. retried = 1;
  1411. goto retry;
  1412. } else {
  1413. IOLog("MacFUSE: filehandle_get failed in mmap (type=%d, err=%d)\n",
  1414. fufh_type, err);
  1415. }
  1416. return EPERM;
  1417. }
  1418. out:
  1419. return 0;
  1420. }
  1421. /*
  1422. struct vnop_mnomap_args {
  1423. struct vnodeop_desc *a_desc;
  1424. vnode_t a_vp;
  1425. vfs_context_t a_context;
  1426. };
  1427. */
  1428. FUSE_VNOP_EXPORT
  1429. int
  1430. fuse_vnop_mnomap(struct vnop_mnomap_args *ap)
  1431. {
  1432. vnode_t vp = ap->a_vp;
  1433. fuse_trace_printf_vnop();
  1434. if (fuse_isdeadfs(vp)) {
  1435. return 0;
  1436. }
  1437. if (fuse_isdirectio(vp)) {
  1438. /*
  1439. * ubc_unmap() doesn't care about the return value.
  1440. */
  1441. return ENODEV;
  1442. }
  1443. /*
  1444. * XXX
  1445. *
  1446. * What behavior do we want here?
  1447. *
  1448. * I once noted that sync() is not going to help here, but I think
  1449. * I've forgotten the context. Need to think about this again.
  1450. *
  1451. * ubc_msync(vp, (off_t)0, ubc_getsize(vp), (off_t*)0, UBC_PUSHDIRTY);
  1452. */
  1453. /*
  1454. * Earlier, we used to go through our vnode's fufh list here, doing
  1455. * something like the following:
  1456. *
  1457. * for (type = 0; type < FUFH_MAXTYPE; type++) {
  1458. * fufh = &(fvdat->fufh[type]);
  1459. * if ((fufh->fufh_flags & FUFH_VALID) &&
  1460. * (fufh->fufh_flags & FUFH_MAPPED)) {
  1461. * fufh->fufh_flags &= ~FUFH_MAPPED;
  1462. * if (fufh->open_count == 0) {
  1463. * (void)fuse_filehandle_put(vp, context, type,
  1464. * FUSE_OP_BACKGROUNDED);
  1465. * }
  1466. * }
  1467. * }
  1468. *
  1469. * Now, cleanup is all taken care of in vnop_inactive/reclaim.
  1470. *
  1471. */
  1472. return 0;
  1473. }
  1474. /*
  1475. struct vnop_offtoblk_args {
  1476. struct vnodeop_desc *a_desc;
  1477. vnode_t a_vp;
  1478. off_t a_offset;
  1479. daddr64_t *a_lblkno;
  1480. };
  1481. */
  1482. FUSE_VNOP_EXPORT
  1483. int
  1484. fuse_vnop_offtoblk(struct vnop_offtoblk_args *ap)
  1485. {
  1486. vnode_t vp = ap->a_vp;
  1487. off_t offset = ap->a_offset;
  1488. daddr64_t *lblknoPtr = ap->a_lblkno;
  1489. struct fuse_data *data;
  1490. fuse_trace_printf_vnop();
  1491. if (fuse_isdeadfs(vp)) {
  1492. return EIO;
  1493. }
  1494. data = fuse_get_mpdata(vnode_mount(vp));
  1495. *lblknoPtr = offset / data->blocksize;
  1496. return 0;
  1497. }
  1498. /*
  1499. struct vnop_open_args {
  1500. struct vnodeop_desc *a_desc;
  1501. vnode_t a_vp;
  1502. int a_mode;
  1503. vfs_context_t a_context;
  1504. };
  1505. */
  1506. FUSE_VNOP_EXPORT
  1507. int
  1508. fuse_vnop_open(struct vnop_open_args *ap)
  1509. {
  1510. vnode_t vp = ap->a_vp;
  1511. int mode = ap->a_mode;
  1512. vfs_context_t context = ap->a_context;
  1513. fufh_type_t fufh_type;
  1514. struct fuse_vnode_data *fvdat;
  1515. struct fuse_filehandle *fufh = NULL;
  1516. struct fuse_filehandle *fufh_rw = NULL;
  1517. int error, isdir = 0;
  1518. long hint = 0;
  1519. fuse_trace_printf_vnop();
  1520. if (fuse_isdeadfs(vp)) {
  1521. return ENXIO;
  1522. }
  1523. #if !M_MACFUSE_ENABLE_FIFOFS
  1524. if (vnode_isfifo(vp)) {
  1525. return EPERM;
  1526. }
  1527. #endif
  1528. CHECK_BLANKET_DENIAL(vp, context, ENOENT);
  1529. fvdat = VTOFUD(vp);
  1530. if (vnode_isdir(vp)) {
  1531. isdir = 1;
  1532. }
  1533. if (isdir) {
  1534. fufh_type = FUFH_RDONLY;
  1535. } else {
  1536. fufh_type = fuse_filehandle_xlate_from_fflags(mode);
  1537. }
  1538. fufh = &(fvdat->fufh[fufh_type]);
  1539. if (!isdir && (fvdat->flag & FN_CREATING)) {
  1540. fuse_lck_mtx_lock(fvdat->createlock);
  1541. if (fvdat->flag & FN_CREATING) { // check again
  1542. if (fvdat->creator == current_thread()) {
  1543. /*
  1544. * For testing the race condition we want to prevent here,
  1545. * try something like the following:
  1546. *
  1547. * int dummyctr = 0;
  1548. *
  1549. * for (; dummyctr < 2048000000; dummyctr++);
  1550. */
  1551. fufh_rw = &(fvdat->fufh[FUFH_RDWR]);
  1552. fufh->open_flags = fufh_rw->open_flags;
  1553. fufh->fh_id = fufh_rw->fh_id;
  1554. /* Note that fufh_rw can be the same as fufh! Order is key. */
  1555. fufh_rw->open_count = 0;
  1556. fufh->open_count = 1;
  1557. /*
  1558. * Creator has picked up stashed handle and moved it to the
  1559. * fufh_type slot.
  1560. */
  1561. fvdat->flag &= ~FN_CREATING;
  1562. fuse_lck_mtx_unlock(fvdat->createlock);
  1563. fuse_wakeup((caddr_t)fvdat->creator); // wake up all
  1564. goto ok; /* return 0 */
  1565. } else {
  1566. /* Contender is going to sleep now. */
  1567. error = fuse_msleep(fvdat->creator, fvdat->createlock,
  1568. PDROP | PINOD | PCATCH, "fuse_open", NULL);
  1569. /*
  1570. * msleep will drop the mutex. since we have PDROP specified,
  1571. * it will NOT regrab the mutex when it returns.
  1572. */
  1573. /* Contender is awake now. */
  1574. if (error) {
  1575. /*
  1576. * Since we specified PCATCH above, we'll be woken up in
  1577. * case a signal arrives. The value of error could be
  1578. * EINTR or ERESTART.
  1579. */
  1580. return error;
  1581. }
  1582. }
  1583. } else {
  1584. fuse_lck_mtx_unlock(fvdat->createlock);
  1585. /* Can proceed from here. */
  1586. }
  1587. }
  1588. if (FUFH_IS_VALID(fufh)) {
  1589. FUFH_USE_INC(fufh);
  1590. FUSE_OSAddAtomic(1, (SInt32 *)&fuse_fh_reuse_count);
  1591. goto ok; /* return 0 */
  1592. }
  1593. error = fuse_filehandle_get(vp, context, fufh_type, mode);
  1594. if (error) {
  1595. IOLog("MacFUSE: filehandle_get failed in open (type=%d, err=%d)\n",
  1596. fufh_type, error);
  1597. if (error == ENOENT) {
  1598. cache_purge(vp);
  1599. }
  1600. return error;
  1601. }
  1602. ok:
  1603. /*
  1604. * Doing this here because when a vnode goes inactive, things like
  1605. * no-cache and no-readahead are cleared by the kernel.
  1606. */
  1607. if ((fufh->fuse_open_flags & FOPEN_DIRECT_IO) || (fuse_isdirectio(vp))) {
  1608. /*
  1609. * direct_io for a vnode implies:
  1610. * - n…