ukfs.c revision 1.20 1 /* $NetBSD: ukfs.c,v 1.20 2009/01/23 15:06:48 pooka Exp $ */
2
3 /*
4 * Copyright (c) 2007, 2008 Antti Kantee. All Rights Reserved.
5 *
6 * Development of this software was supported by the
7 * Finnish Cultural Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 /*
32 * This library enables access to files systems directly without
33 * involving system calls.
34 */
35
36 #ifdef __linux__
37 #define _XOPEN_SOURCE 500
38 #define _BSD_SOURCE
39 #define _FILE_OFFSET_BITS 64
40 #endif
41
42 #include <sys/param.h>
43 #include <sys/queue.h>
44 #include <sys/stat.h>
45 #include <sys/sysctl.h>
46 #include <sys/mount.h>
47
48 #include <assert.h>
49 #include <dirent.h>
50 #include <dlfcn.h>
51 #include <err.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <pthread.h>
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <string.h>
58 #include <unistd.h>
59 #include <stdint.h>
60
61 #include <rump/ukfs.h>
62
63 #include <rump/rump.h>
64 #include <rump/rump_syscalls.h>
65
66 #define UKFS_MODE_DEFAULT 0555
67
68 struct ukfs {
69 struct mount *ukfs_mp;
70 struct vnode *ukfs_rvp;
71
72 pthread_spinlock_t ukfs_spin;
73 pid_t ukfs_nextpid;
74 struct vnode *ukfs_cdir;
75 int ukfs_devfd;
76 };
77
78 struct mount *
79 ukfs_getmp(struct ukfs *ukfs)
80 {
81
82 return ukfs->ukfs_mp;
83 }
84
85 struct vnode *
86 ukfs_getrvp(struct ukfs *ukfs)
87 {
88 struct vnode *rvp;
89
90 rvp = ukfs->ukfs_rvp;
91 rump_vp_incref(rvp);
92
93 return rvp;
94 }
95
96 #ifdef DONT_WANT_PTHREAD_LINKAGE
97 #define pthread_spin_lock(a)
98 #define pthread_spin_unlock(a)
99 #define pthread_spin_init(a,b)
100 #define pthread_spin_destroy(a)
101 #endif
102
103 static pid_t
104 nextpid(struct ukfs *ukfs)
105 {
106 pid_t npid;
107
108 pthread_spin_lock(&ukfs->ukfs_spin);
109 if (ukfs->ukfs_nextpid == 0)
110 ukfs->ukfs_nextpid++;
111 npid = ukfs->ukfs_nextpid++;
112 pthread_spin_unlock(&ukfs->ukfs_spin);
113
114 return npid;
115 }
116
117 static void
118 precall(struct ukfs *ukfs)
119 {
120 struct vnode *rvp, *cvp;
121
122 rump_setup_curlwp(nextpid(ukfs), 1, 1);
123 rvp = ukfs_getrvp(ukfs);
124 pthread_spin_lock(&ukfs->ukfs_spin);
125 cvp = ukfs->ukfs_cdir;
126 pthread_spin_unlock(&ukfs->ukfs_spin);
127 rump_rcvp_set(rvp, cvp); /* takes refs */
128 rump_vp_rele(rvp);
129 }
130
131 static void
132 postcall(struct ukfs *ukfs)
133 {
134 struct vnode *rvp;
135
136 rvp = ukfs_getrvp(ukfs);
137 rump_rcvp_set(NULL, rvp);
138 rump_vp_rele(rvp);
139 rump_clear_curlwp();
140 }
141
142 int
143 _ukfs_init(int version)
144 {
145 int rv;
146
147 if (version != UKFS_VERSION) {
148 printf("incompatible ukfs version, %d vs. %d\n",
149 version, UKFS_VERSION);
150 errno = EPROGMISMATCH;
151 return -1;
152 }
153
154 if ((rv = rump_init()) != 0) {
155 errno = rv;
156 return -1;
157 }
158
159 return 0;
160 }
161
162 struct ukfs *
163 ukfs_mount(const char *vfsname, const char *devpath, const char *mountpath,
164 int mntflags, void *arg, size_t alen)
165 {
166 struct ukfs *fs = NULL;
167 struct vfsops *vfsops;
168 struct mount *mp = NULL;
169 int rv = 0, devfd = -1, rdonly;
170
171 vfsops = rump_vfs_getopsbyname(vfsname);
172 if (vfsops == NULL) {
173 rv = ENODEV;
174 goto out;
175 }
176
177 /*
178 * Try open and lock the device. if we can't open it, assume
179 * it's a file system which doesn't use a real device and let
180 * it slide. The mount will fail anyway if the fs requires a
181 * device.
182 *
183 * XXX: strictly speaking this is not 100% correct, as virtual
184 * file systems can use a device path which does exist and can
185 * be opened. E.g. tmpfs should be mountable multiple times
186 * with "device" path "/swap", but now isn't. But I think the
187 * chances are so low that it's currently acceptable to let
188 * this one slip.
189 */
190 rdonly = mntflags & MNT_RDONLY;
191 devfd = open(devpath, rdonly ? O_RDONLY : O_RDWR);
192 if (devfd != -1) {
193 if (flock(devfd, LOCK_NB | (rdonly ? LOCK_SH:LOCK_EX)) == -1) {
194 warnx("ukfs_mount: cannot get %s lock on device",
195 rdonly ? "shared" : "exclusive");
196 close(devfd);
197 devfd = -1;
198 rv = errno;
199 goto out;
200 }
201 }
202
203 fs = malloc(sizeof(struct ukfs));
204 if (fs == NULL) {
205 rv = ENOMEM;
206 goto out;
207 }
208 memset(fs, 0, sizeof(struct ukfs));
209 mp = rump_mnt_init(vfsops, mntflags);
210
211 rump_fakeblk_register(devpath);
212 rv = rump_mnt_mount(mp, mountpath, arg, &alen);
213 rump_fakeblk_deregister(devpath);
214 if (rv) {
215 goto out;
216 }
217 rv = rump_vfs_root(mp, &fs->ukfs_rvp, 0);
218 if (rv) {
219 goto out;
220 }
221 fs->ukfs_cdir = ukfs_getrvp(fs);
222
223 fs->ukfs_mp = mp;
224 pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED);
225 fs->ukfs_devfd = devfd;
226 assert(rv == 0);
227
228 out:
229 if (rv) {
230 if (mp)
231 rump_mnt_destroy(mp);
232 if (fs)
233 free(fs);
234 errno = rv;
235 fs = NULL;
236 if (devfd != -1) {
237 flock(devfd, LOCK_UN);
238 close(devfd);
239 }
240 }
241
242 return fs;
243 }
244
245 void
246 ukfs_release(struct ukfs *fs, int flags)
247 {
248 int rv;
249
250 if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) {
251 kauth_cred_t cred;
252
253 rump_vp_rele(fs->ukfs_cdir);
254 cred = rump_cred_suserget();
255 rv = rump_vfs_sync(fs->ukfs_mp, 1, cred);
256 rump_cred_suserput(cred);
257 rump_vp_recycle_nokidding(ukfs_getrvp(fs));
258 rv |= rump_vfs_unmount(fs->ukfs_mp, 0);
259 assert(rv == 0);
260 }
261
262 rump_vfs_syncwait(fs->ukfs_mp);
263 rump_mnt_destroy(fs->ukfs_mp);
264
265 pthread_spin_destroy(&fs->ukfs_spin);
266 if (fs->ukfs_devfd != -1) {
267 flock(fs->ukfs_devfd, LOCK_UN);
268 close(fs->ukfs_devfd);
269 }
270 free(fs);
271 }
272
273 #define STDCALL(ukfs, thecall) \
274 int rv = 0; \
275 \
276 precall(ukfs); \
277 thecall; \
278 postcall(ukfs); \
279 if (rv) { \
280 errno = rv; \
281 return -1; \
282 } \
283 return 0;
284
285 int
286 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off,
287 uint8_t *buf, size_t bufsize)
288 {
289 struct uio *uio;
290 struct vnode *vp;
291 size_t resid;
292 kauth_cred_t cred;
293 int rv, eofflag;
294
295 precall(ukfs);
296 rv = rump_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
297 NULL, &vp, NULL);
298 postcall(ukfs);
299 if (rv)
300 goto out;
301
302 uio = rump_uio_setup(buf, bufsize, *off, RUMPUIO_READ);
303 cred = rump_cred_suserget();
304 rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL);
305 rump_cred_suserput(cred);
306 RUMP_VOP_UNLOCK(vp, 0);
307 *off = rump_uio_getoff(uio);
308 resid = rump_uio_free(uio);
309 rump_vp_rele(vp);
310
311 out:
312 if (rv) {
313 errno = rv;
314 return -1;
315 }
316
317 /* LINTED: not totally correct return type, but follows syscall */
318 return bufsize - resid;
319 }
320
321 ssize_t
322 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off,
323 uint8_t *buf, size_t bufsize)
324 {
325 int fd, rv = 0, dummy;
326 ssize_t xfer = -1; /* XXXgcc */
327
328 precall(ukfs);
329 fd = rump_sys_open(filename, RUMP_O_RDONLY, 0, &rv);
330 if (rv)
331 goto out;
332
333 xfer = rump_sys_pread(fd, buf, bufsize, 0, off, &rv);
334 rump_sys_close(fd, &dummy);
335
336 out:
337 postcall(ukfs);
338 if (rv) {
339 errno = rv;
340 return -1;
341 }
342 return xfer;
343 }
344
345 ssize_t
346 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off,
347 uint8_t *buf, size_t bufsize)
348 {
349 int fd, rv = 0, dummy;
350 ssize_t xfer = -1; /* XXXgcc */
351
352 precall(ukfs);
353 fd = rump_sys_open(filename, RUMP_O_WRONLY, 0, &rv);
354 if (rv)
355 goto out;
356
357 /* write and commit */
358 xfer = rump_sys_pwrite(fd, buf, bufsize, 0, off, &rv);
359 if (rv == 0)
360 rump_sys_fsync(fd, &dummy);
361
362 rump_sys_close(fd, &dummy);
363
364 out:
365 postcall(ukfs);
366 if (rv) {
367 errno = rv;
368 return -1;
369 }
370 return xfer;
371 }
372
373 int
374 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode)
375 {
376 int rv, fd, dummy;
377
378 precall(ukfs);
379 fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode, &rv);
380 rump_sys_close(fd, &dummy);
381
382 postcall(ukfs);
383 if (rv) {
384 errno = rv;
385 return -1;
386 }
387 return 0;
388 }
389
390 int
391 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev)
392 {
393
394 STDCALL(ukfs, rump_sys_mknod(path, mode, dev, &rv));
395 }
396
397 int
398 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode)
399 {
400
401 STDCALL(ukfs, rump_sys_mkfifo(path, mode, &rv));
402 }
403
404 int
405 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode)
406 {
407
408 STDCALL(ukfs, rump_sys_mkdir(filename, mode, &rv));
409 }
410
411 int
412 ukfs_remove(struct ukfs *ukfs, const char *filename)
413 {
414
415 STDCALL(ukfs, rump_sys_unlink(filename, &rv));
416 }
417
418 int
419 ukfs_rmdir(struct ukfs *ukfs, const char *filename)
420 {
421
422 STDCALL(ukfs, rump_sys_rmdir(filename, &rv));
423 }
424
425 int
426 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create)
427 {
428
429 STDCALL(ukfs, rump_sys_link(filename, f_create, &rv));
430 }
431
432 int
433 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname)
434 {
435
436 STDCALL(ukfs, rump_sys_symlink(filename, linkname, &rv));
437 }
438
439 ssize_t
440 ukfs_readlink(struct ukfs *ukfs, const char *filename,
441 char *linkbuf, size_t buflen)
442 {
443 ssize_t rv;
444 int myerr = 0;
445
446 precall(ukfs);
447 rv = rump_sys_readlink(filename, linkbuf, buflen, &myerr);
448 postcall(ukfs);
449 if (myerr) {
450 errno = myerr;
451 return -1;
452 }
453 return rv;
454 }
455
456 int
457 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to)
458 {
459
460 STDCALL(ukfs, rump_sys_rename(from, to, &rv));
461 }
462
463 int
464 ukfs_chdir(struct ukfs *ukfs, const char *path)
465 {
466 struct vnode *newvp, *oldvp;
467 int rv;
468
469 precall(ukfs);
470 rump_sys_chdir(path, &rv);
471 if (rv)
472 goto out;
473
474 newvp = rump_cdir_get();
475 pthread_spin_lock(&ukfs->ukfs_spin);
476 oldvp = ukfs->ukfs_cdir;
477 ukfs->ukfs_cdir = newvp;
478 pthread_spin_unlock(&ukfs->ukfs_spin);
479 if (oldvp)
480 rump_vp_rele(oldvp);
481
482 out:
483 postcall(ukfs);
484 if (rv) {
485 errno = rv;
486 return -1;
487 }
488 return 0;
489 }
490
491 int
492 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
493 {
494
495 STDCALL(ukfs, rump_sys_stat(filename, file_stat, &rv));
496 }
497
498 int
499 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
500 {
501
502 STDCALL(ukfs, rump_sys_lstat(filename, file_stat, &rv));
503 }
504
505 int
506 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode)
507 {
508
509 STDCALL(ukfs, rump_sys_chmod(filename, mode, &rv));
510 }
511
512 int
513 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode)
514 {
515
516 STDCALL(ukfs, rump_sys_lchmod(filename, mode, &rv));
517 }
518
519 int
520 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
521 {
522
523 STDCALL(ukfs, rump_sys_chown(filename, uid, gid, &rv));
524 }
525
526 int
527 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
528 {
529
530 STDCALL(ukfs, rump_sys_lchown(filename, uid, gid, &rv));
531 }
532
533 int
534 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags)
535 {
536
537 STDCALL(ukfs, rump_sys_chflags(filename, flags, &rv));
538 }
539
540 int
541 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags)
542 {
543
544 STDCALL(ukfs, rump_sys_lchflags(filename, flags, &rv));
545 }
546
547 int
548 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr)
549 {
550
551 STDCALL(ukfs, rump_sys_utimes(filename, tptr, &rv));
552 }
553
554 int
555 ukfs_lutimes(struct ukfs *ukfs, const char *filename,
556 const struct timeval *tptr)
557 {
558
559 STDCALL(ukfs, rump_sys_lutimes(filename, tptr, &rv));
560 }
561
562 /*
563 * Dynamic module support
564 */
565
566 /* load one library */
567
568 /*
569 * XXX: the dlerror stuff isn't really threadsafe, but then again I
570 * can't protect against other threads calling dl*() outside of ukfs,
571 * so just live with it being flimsy
572 */
573 #define UFSLIB "librumpfs_ufs.so"
574 int
575 ukfs_modload(const char *fname)
576 {
577 void *handle, *thesym;
578 struct stat sb;
579 const char *p;
580 int error;
581
582 if (stat(fname, &sb) == -1)
583 return -1;
584
585 handle = dlopen(fname, RTLD_GLOBAL);
586 if (handle == NULL) {
587 const char *dlmsg = dlerror();
588 if (strstr(dlmsg, "Undefined symbol"))
589 return 0;
590 warnx("dlopen %s failed: %s\n", fname, dlmsg);
591 /* XXXerrno */
592 return -1;
593 }
594
595 /*
596 * XXX: the ufs module is not loaded in the same fashion as the
597 * others. But we can't do dlclose() for it, since that would
598 * lead to not being able to load ffs/ext2fs/lfs. Hence hardcode
599 * and kludge around the issue for now. But this should really
600 * be fixed by fixing sys/ufs/ufs to be a kernel module.
601 */
602 if ((p = strrchr(fname, '/')) != NULL)
603 p++;
604 else
605 p = fname;
606 if (strcmp(p, UFSLIB) == 0)
607 return 1;
608
609 thesym = dlsym(handle, "__start_link_set_modules");
610 if (thesym) {
611 error = rump_module_load(thesym);
612 if (error)
613 goto errclose;
614 return 1;
615 }
616 error = EINVAL;
617
618 errclose:
619 dlclose(handle);
620 errno = error;
621 return -1;
622 }
623
624 struct loadfail {
625 char *pname;
626
627 LIST_ENTRY(loadfail) entries;
628 };
629
630 #define RUMPFSMOD_PREFIX "librumpfs_"
631 #define RUMPFSMOD_SUFFIX ".so"
632
633 int
634 ukfs_modload_dir(const char *dir)
635 {
636 char nbuf[MAXPATHLEN+1], *p;
637 struct dirent entry, *result;
638 DIR *libdir;
639 struct loadfail *lf, *nlf;
640 int error, nloaded = 0, redo;
641 LIST_HEAD(, loadfail) lfs;
642
643 libdir = opendir(dir);
644 if (libdir == NULL)
645 return -1;
646
647 LIST_INIT(&lfs);
648 for (;;) {
649 if ((error = readdir_r(libdir, &entry, &result)) != 0)
650 break;
651 if (!result)
652 break;
653 if (strncmp(result->d_name, RUMPFSMOD_PREFIX,
654 strlen(RUMPFSMOD_PREFIX)) != 0)
655 continue;
656 if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL)
657 || strlen(p) != strlen(RUMPFSMOD_SUFFIX))
658 continue;
659 strlcpy(nbuf, dir, sizeof(nbuf));
660 strlcat(nbuf, "/", sizeof(nbuf));
661 strlcat(nbuf, result->d_name, sizeof(nbuf));
662 switch (ukfs_modload(nbuf)) {
663 case 0:
664 lf = malloc(sizeof(*lf));
665 if (lf == NULL) {
666 error = ENOMEM;
667 break;
668 }
669 lf->pname = strdup(nbuf);
670 if (lf->pname == NULL) {
671 free(lf);
672 error = ENOMEM;
673 break;
674 }
675 LIST_INSERT_HEAD(&lfs, lf, entries);
676 break;
677 case 1:
678 nloaded++;
679 break;
680 default:
681 /* ignore errors */
682 break;
683 }
684 }
685 closedir(libdir);
686 if (error && nloaded != 0)
687 error = 0;
688
689 /*
690 * El-cheapo dependency calculator. Just try to load the
691 * modules n times in a loop
692 */
693 for (redo = 1; redo;) {
694 redo = 0;
695 nlf = LIST_FIRST(&lfs);
696 while ((lf = nlf) != NULL) {
697 nlf = LIST_NEXT(lf, entries);
698 if (ukfs_modload(lf->pname) == 1) {
699 nloaded++;
700 redo = 1;
701 LIST_REMOVE(lf, entries);
702 free(lf->pname);
703 free(lf);
704 }
705 }
706 }
707
708 while ((lf = LIST_FIRST(&lfs)) != NULL) {
709 LIST_REMOVE(lf, entries);
710 free(lf->pname);
711 free(lf);
712 }
713
714 if (error && nloaded == 0) {
715 errno = error;
716 return -1;
717 }
718
719 return nloaded;
720 }
721
722 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */
723 ssize_t
724 ukfs_vfstypes(char *buf, size_t buflen)
725 {
726 int mib[3];
727 struct sysctlnode q, ans[128];
728 size_t alen;
729 int error, i;
730
731 mib[0] = CTL_VFS;
732 mib[1] = VFS_GENERIC;
733 mib[2] = CTL_QUERY;
734 alen = sizeof(ans);
735
736 memset(&q, 0, sizeof(q));
737 q.sysctl_flags = SYSCTL_VERSION;
738
739 if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q), &error) == -1){
740 errno = error;
741 return -1;
742 }
743
744 for (i = 0; i < alen/sizeof(ans[0]); i++)
745 if (strcmp("fstypes", ans[i].sysctl_name) == 0)
746 break;
747 if (i == alen/sizeof(ans[0])) {
748 errno = ENXIO;
749 return -1;
750 }
751
752 mib[0] = CTL_VFS;
753 mib[1] = VFS_GENERIC;
754 mib[2] = ans[i].sysctl_num;
755
756 if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0, &error) == -1) {
757 errno = error;
758 return -1;
759 }
760
761 return buflen;
762 }
763
764 /*
765 * Utilities
766 */
767 int
768 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode)
769 {
770 char *f1, *f2;
771 int rv;
772 mode_t mask;
773 bool end;
774
775 /*ukfs_umask((mask = ukfs_umask(0)));*/
776 umask((mask = umask(0)));
777
778 f1 = f2 = strdup(pathname);
779 if (f1 == NULL) {
780 errno = ENOMEM;
781 return -1;
782 }
783
784 end = false;
785 for (;;) {
786 /* find next component */
787 f2 += strspn(f2, "/");
788 f2 += strcspn(f2, "/");
789 if (*f2 == '\0')
790 end = true;
791 else
792 *f2 = '\0';
793
794 rv = ukfs_mkdir(ukfs, f1, mode & ~mask);
795 if (errno == EEXIST)
796 rv = 0;
797
798 if (rv == -1 || *f2 != '\0' || end)
799 break;
800
801 *f2 = '/';
802 }
803
804 free(f1);
805
806 return rv;
807 }
808