xref: /netbsd-src/lib/libukfs/ukfs.c (revision 388550b026d49b7f7b7480b1113bf82bb8d6a480)
1 /*	$NetBSD: ukfs.c,v 1.60 2022/04/19 20:32:17 rillig Exp $	*/
2 
3 /*
4  * Copyright (c) 2007, 2008, 2009  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/rumpvnode_if.h>
65 #include <rump/rump_syscalls.h>
66 
67 #include "ukfs_int_disklabel.h"
68 
69 #define UKFS_MODE_DEFAULT 0555
70 
71 struct ukfs {
72 	pthread_spinlock_t ukfs_spin;
73 
74 	struct mount *ukfs_mp;
75 	struct lwp *ukfs_lwp;
76 	void *ukfs_specific;
77 
78 	int ukfs_devfd;
79 
80 	char *ukfs_devpath;
81 	char *ukfs_mountpath;
82 	char *ukfs_cwd;
83 
84 	struct ukfs_part *ukfs_part;
85 };
86 
87 static int builddirs(const char *, mode_t,
88     int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *);
89 
90 struct mount *
ukfs_getmp(struct ukfs * ukfs)91 ukfs_getmp(struct ukfs *ukfs)
92 {
93 
94 	return ukfs->ukfs_mp;
95 }
96 
97 void
ukfs_setspecific(struct ukfs * ukfs,void * priv)98 ukfs_setspecific(struct ukfs *ukfs, void *priv)
99 {
100 
101 	ukfs->ukfs_specific = priv;
102 }
103 
104 void *
ukfs_getspecific(struct ukfs * ukfs)105 ukfs_getspecific(struct ukfs *ukfs)
106 {
107 
108 	return ukfs->ukfs_specific;
109 }
110 
111 #ifdef DONT_WANT_PTHREAD_LINKAGE
112 #define pthread_spin_lock(a)
113 #define pthread_spin_unlock(a)
114 #define pthread_spin_init(a,b)
115 #define pthread_spin_destroy(a)
116 #endif
117 
118 static int
precall(struct ukfs * ukfs,struct lwp ** curlwp)119 precall(struct ukfs *ukfs, struct lwp **curlwp)
120 {
121 
122 	/* save previous.  ensure start from pristine context */
123 	*curlwp = rump_pub_lwproc_curlwp();
124 	if (*curlwp)
125 		rump_pub_lwproc_switch(ukfs->ukfs_lwp);
126 	rump_pub_lwproc_rfork(RUMP_RFCFDG);
127 
128 	if (rump_sys_chroot(ukfs->ukfs_mountpath) == -1)
129 		return errno;
130 	if (rump_sys_chdir(ukfs->ukfs_cwd) == -1)
131 		return errno;
132 
133 	return 0;
134 }
135 
136 static void
postcall(struct lwp * curlwp)137 postcall(struct lwp *curlwp)
138 {
139 
140 	rump_pub_lwproc_releaselwp();
141 	if (curlwp)
142 		rump_pub_lwproc_switch(curlwp);
143 }
144 
145 #define PRECALL()							\
146 struct lwp *ukfs_curlwp;						\
147 do {									\
148 	int ukfs_rv;							\
149 	if ((ukfs_rv = precall(ukfs, &ukfs_curlwp)) != 0) {		\
150 		errno = ukfs_rv;					\
151 		return -1;						\
152 	}								\
153 } while (0)
154 
155 #define POSTCALL() postcall(ukfs_curlwp);
156 
157 struct ukfs_part {
158 	pthread_spinlock_t part_lck;
159 	int part_refcount;
160 
161 	int part_type;
162 	char part_labelchar;
163 	off_t part_devoff;
164 	off_t part_devsize;
165 };
166 
167 enum ukfs_parttype { UKFS_PART_NONE, UKFS_PART_DISKLABEL, UKFS_PART_OFFSET };
168 
169 static struct ukfs_part ukfs__part_none = {
170 	.part_type = UKFS_PART_NONE,
171 	.part_devoff = 0,
172 	.part_devsize = RUMP_ETFS_SIZE_ENDOFF,
173 };
174 static struct ukfs_part ukfs__part_na;
175 struct ukfs_part *ukfs_part_none = &ukfs__part_none;
176 struct ukfs_part *ukfs_part_na = &ukfs__part_na;
177 
178 #define PART2LOCKSIZE(len) ((len) == RUMP_ETFS_SIZE_ENDOFF ? 0 : (len))
179 
180 int
_ukfs_init(int version)181 _ukfs_init(int version)
182 {
183 	int rv;
184 
185 	if (version != UKFS_VERSION) {
186 		errno = EPROGMISMATCH;
187 		warn("incompatible ukfs version, %d vs. %d",
188 		    version, UKFS_VERSION);
189 		return -1;
190 	}
191 
192 	if ((rv = rump_init()) != 0) {
193 		errno = rv;
194 		return -1;
195 	}
196 
197 	return 0;
198 }
199 
200 /*ARGSUSED*/
201 static int
rumpmkdir(struct ukfs * dummy,const char * path,mode_t mode)202 rumpmkdir(struct ukfs *dummy, const char *path, mode_t mode)
203 {
204 
205 	return rump_sys_mkdir(path, mode);
206 }
207 
208 int
ukfs_part_probe(char * devpath,struct ukfs_part ** partp)209 ukfs_part_probe(char *devpath, struct ukfs_part **partp)
210 {
211 	struct ukfs_part *part;
212 	char *p;
213 	int error = 0;
214 	int devfd = -1;
215 
216 	if ((p = strstr(devpath, UKFS_PARTITION_SCANMAGIC)) != NULL) {
217 		warnx("ukfs: %%PART is deprecated.  use "
218 		    "%%DISKLABEL instead");
219 		errno = ENODEV;
220 		return -1;
221 	}
222 
223 	part = malloc(sizeof(*part));
224 	if (part == NULL) {
225 		errno = ENOMEM;
226 		return -1;
227 	}
228 	if (pthread_spin_init(&part->part_lck, PTHREAD_PROCESS_PRIVATE) == -1) {
229 		error = errno;
230 		free(part);
231 		errno = error;
232 		return -1;
233 	}
234 	part->part_type = UKFS_PART_NONE;
235 	part->part_refcount = 1;
236 
237 	/*
238 	 * Check for magic in pathname:
239 	 *   disklabel: /regularpath%DISKLABEL:labelchar%\0
240 	 *     offsets: /regularpath%OFFSET:start,end%\0
241 	 */
242 #define MAGICADJ_DISKLABEL(p, n) (p+sizeof(UKFS_DISKLABEL_SCANMAGIC)-1+n)
243 	if ((p = strstr(devpath, UKFS_DISKLABEL_SCANMAGIC)) != NULL
244 	    && strlen(p) == UKFS_DISKLABEL_MAGICLEN
245 	    && *(MAGICADJ_DISKLABEL(p,1)) == '%') {
246 		if (*(MAGICADJ_DISKLABEL(p,0)) >= 'a' &&
247 		    *(MAGICADJ_DISKLABEL(p,0)) < 'a' + UKFS_MAXPARTITIONS) {
248 			struct ukfs__disklabel dl;
249 			struct ukfs__partition *pp;
250 			int imswapped;
251 			char buf[65536];
252 			char labelchar = *(MAGICADJ_DISKLABEL(p,0));
253 			int partition = labelchar - 'a';
254 			uint32_t poffset, psize;
255 
256 			*p = '\0';
257 			devfd = open(devpath, O_RDONLY);
258 			if (devfd == -1) {
259 				error = errno;
260 				goto out;
261 			}
262 
263 			/* Locate the disklabel and find the partition. */
264 			if (pread(devfd, buf, sizeof(buf), 0) == -1) {
265 				error = errno;
266 				goto out;
267 			}
268 
269 			if (ukfs__disklabel_scan(&dl, &imswapped,
270 			    buf, sizeof(buf)) != 0) {
271 				error = ENOENT;
272 				goto out;
273 			}
274 
275 			if (dl.d_npartitions < partition) {
276 				error = ENOENT;
277 				goto out;
278 			}
279 
280 			pp = &dl.d_partitions[partition];
281 			part->part_type = UKFS_PART_DISKLABEL;
282 			part->part_labelchar = labelchar;
283 			if (imswapped) {
284 				poffset = bswap32(pp->p_offset);
285 				psize = bswap32(pp->p_size);
286 			} else {
287 				poffset = pp->p_offset;
288 				psize = pp->p_size;
289 			}
290 			part->part_devoff = poffset << DEV_BSHIFT;
291 			part->part_devsize = psize << DEV_BSHIFT;
292 		} else {
293 			error = EINVAL;
294 		}
295 #define MAGICADJ_OFFSET(p, n) (p+sizeof(UKFS_OFFSET_SCANMAGIC)-1+n)
296 	} else if (((p = strstr(devpath, UKFS_OFFSET_SCANMAGIC)) != NULL)
297 	    && (strlen(p) >= UKFS_OFFSET_MINLEN)) {
298 		char *comma, *pers, *ep, *nptr;
299 		u_quad_t val;
300 
301 		comma = strchr(p, ',');
302 		if (comma == NULL) {
303 			error = EINVAL;
304 			goto out;
305 		}
306 		pers = strchr(comma, '%');
307 		if (pers == NULL) {
308 			error = EINVAL;
309 			goto out;
310 		}
311 		*comma = '\0';
312 		*pers = '\0';
313 		*p = '\0';
314 
315 		nptr = MAGICADJ_OFFSET(p,0);
316 		/* check if string is negative */
317 		if (*nptr == '-') {
318 			error = ERANGE;
319 			goto out;
320 		}
321 		val = strtouq(nptr, &ep, 10);
322 		if (val == UQUAD_MAX) {
323 			error = ERANGE;
324 			goto out;
325 		}
326 		if (*ep != '\0') {
327 			error = EADDRNOTAVAIL; /* creative ;) */
328 			goto out;
329 		}
330 		part->part_devoff = val;
331 
332 		/* omstart */
333 
334 		nptr = comma+1;
335 		/* check if string is negative */
336 		if (*nptr == '-') {
337 			error = ERANGE;
338 			goto out;
339 		}
340 		val = strtouq(nptr, &ep, 10);
341 		if (val == UQUAD_MAX) {
342 			error = ERANGE;
343 			goto out;
344 		}
345 		if (*ep != '\0') {
346 			error = EADDRNOTAVAIL; /* creative ;) */
347 			goto out;
348 		}
349 		part->part_devsize = val;
350 		part->part_type = UKFS_PART_OFFSET;
351 	} else {
352 		ukfs_part_release(part);
353 		part = ukfs_part_none;
354 	}
355 
356  out:
357 	if (devfd != -1)
358 		close(devfd);
359 	if (error) {
360 		free(part);
361 		errno = error;
362 	} else {
363 		*partp = part;
364 	}
365 
366 	return error ? -1 : 0;
367 }
368 
369 int
ukfs_part_tostring(struct ukfs_part * part,char * str,size_t strsize)370 ukfs_part_tostring(struct ukfs_part *part, char *str, size_t strsize)
371 {
372 	int rv;
373 
374 	*str = '\0';
375 	/* "pseudo" values */
376 	if (part == ukfs_part_na) {
377 		errno = EINVAL;
378 		return -1;
379 	}
380 	if (part == ukfs_part_none)
381 		return 0;
382 
383 	rv = 0;
384 	switch (part->part_type) {
385 	case UKFS_PART_NONE:
386 		break;
387 
388 	case UKFS_PART_DISKLABEL:
389 		snprintf(str, strsize, "%%DISKLABEL:%c%%",part->part_labelchar);
390 		rv = 1;
391 		break;
392 
393 	case UKFS_PART_OFFSET:
394 		snprintf(str, strsize, "[%llu,%llu]",
395 		    (unsigned long long)part->part_devoff,
396 		    (unsigned long long)(part->part_devoff+part->part_devsize));
397 		rv = 1;
398 		break;
399 	}
400 
401 	return rv;
402 }
403 
404 static void
unlockdev(int fd,struct ukfs_part * part)405 unlockdev(int fd, struct ukfs_part *part)
406 {
407 	struct flock flarg;
408 
409 	if (part == ukfs_part_na)
410 		return;
411 
412 	memset(&flarg, 0, sizeof(flarg));
413 	flarg.l_type = F_UNLCK;
414 	flarg.l_whence = SEEK_SET;
415 	flarg.l_start = part->part_devoff;
416 	flarg.l_len = PART2LOCKSIZE(part->part_devsize);
417 	if (fcntl(fd, F_SETLK, &flarg) == -1)
418 		warn("ukfs: cannot unlock device file");
419 }
420 
421 /*
422  * Open the disk file and flock it.  Also, if we are operation on
423  * an embedded partition, find the partition offset and size from
424  * the disklabel.
425  *
426  * We hard-fail only in two cases:
427  *  1) we failed to get the partition info out (don't know what offset
428  *     to mount from)
429  *  2) we failed to flock the source device (i.e. fcntl() fails,
430  *     not e.g. open() before it)
431  *
432  * Otherwise we let the code proceed to mount and let the file system
433  * throw the proper error.  The only questionable bit is that if we
434  * soft-fail before flock and mount does succeed...
435  *
436  * Returns: -1 error (errno reports error code)
437  *           0 success
438  *
439  * dfdp: -1  device is not open
440  *        n  device is open
441  */
442 static int
process_diskdevice(const char * devpath,struct ukfs_part * part,int rdonly,int * dfdp)443 process_diskdevice(const char *devpath, struct ukfs_part *part, int rdonly,
444 	int *dfdp)
445 {
446 	struct stat sb;
447 	int rv = 0, devfd;
448 
449 	/* defaults */
450 	*dfdp = -1;
451 
452 	devfd = open(devpath, rdonly ? O_RDONLY : O_RDWR);
453 	if (devfd == -1) {
454 		rv = errno;
455 		goto out;
456 	}
457 
458 	if (fstat(devfd, &sb) == -1) {
459 		rv = errno;
460 		goto out;
461 	}
462 
463 	/*
464 	 * We do this only for non-block device since the
465 	 * (NetBSD) kernel allows block device open only once.
466 	 * We also need to close the device for fairly obvious reasons.
467 	 */
468 	if (!S_ISBLK(sb.st_mode)) {
469 		struct flock flarg;
470 
471 		memset(&flarg, 0, sizeof(flarg));
472 		flarg.l_type = rdonly ? F_RDLCK : F_WRLCK;
473 		flarg.l_whence = SEEK_SET;
474 		flarg.l_start = part->part_devoff;
475 		flarg.l_len = PART2LOCKSIZE(part->part_devsize);
476 		if (fcntl(devfd, F_SETLK, &flarg) == -1) {
477 			pid_t holder;
478 			int sverrno;
479 
480 			sverrno = errno;
481 			if (fcntl(devfd, F_GETLK, &flarg) != 1)
482 				holder = flarg.l_pid;
483 			else
484 				holder = -1;
485 			warnx("ukfs_mount: cannot lock device.  held by pid %d",
486 			    holder);
487 			rv = sverrno;
488 			goto out;
489 		}
490 	} else {
491 		close(devfd);
492 		devfd = -1;
493 	}
494 	*dfdp = devfd;
495 
496  out:
497 	if (rv) {
498 		if (devfd != -1)
499 			close(devfd);
500 	}
501 
502 	return rv;
503 }
504 
505 struct mountinfo {
506 	const char *mi_vfsname;
507 	const char *mi_mountpath;
508 	int mi_mntflags;
509 	void *mi_arg;
510 	size_t mi_alen;
511 	int *mi_error;
512 };
513 static void *
mfs_mounter(void * arg)514 mfs_mounter(void *arg)
515 {
516 	struct mountinfo *mi = arg;
517 	int rv;
518 
519 	rv = rump_sys_mount(mi->mi_vfsname, mi->mi_mountpath, mi->mi_mntflags,
520 	    mi->mi_arg, mi->mi_alen);
521 	if (rv) {
522 		warn("mfs mount failed.  fix me.");
523 		abort(); /* XXX */
524 	}
525 
526 	return NULL;
527 }
528 
529 static struct ukfs *
doukfsmount(const char * vfsname,const char * devpath,struct ukfs_part * part,const char * mountpath,int mntflags,void * arg,size_t alen)530 doukfsmount(const char *vfsname, const char *devpath, struct ukfs_part *part,
531 	const char *mountpath, int mntflags, void *arg, size_t alen)
532 {
533 	struct ukfs *fs = NULL;
534 	struct lwp *curlwp;
535 	int rv = 0, devfd = -1;
536 	int mounted = 0;
537 	int regged = 0;
538 
539 	pthread_spin_lock(&part->part_lck);
540 	part->part_refcount++;
541 	pthread_spin_unlock(&part->part_lck);
542 	if (part != ukfs_part_na) {
543 		if ((rv = process_diskdevice(devpath, part,
544 		    mntflags & MNT_RDONLY, &devfd)) != 0)
545 			goto out;
546 	}
547 
548 	fs = malloc(sizeof(struct ukfs));
549 	if (fs == NULL) {
550 		rv = ENOMEM;
551 		goto out;
552 	}
553 	memset(fs, 0, sizeof(struct ukfs));
554 
555 	/* create our mountpoint.  this is never removed. */
556 	if (builddirs(mountpath, 0777, rumpmkdir, NULL) == -1) {
557 		if (errno != EEXIST) {
558 			rv = errno;
559 			goto out;
560 		}
561 	}
562 
563 	if (part != ukfs_part_na) {
564 		/* LINTED */
565 		rv = rump_pub_etfs_register_withsize(devpath, devpath,
566 		    RUMP_ETFS_BLK, part->part_devoff, part->part_devsize);
567 		if (rv) {
568 			goto out;
569 		}
570 		regged = 1;
571 	}
572 
573 	/*
574 	 * MFS is special since mount(2) doesn't return.  Hence, we
575 	 * create a thread here.  Could fix mfs to return, but there's
576 	 * too much history for me to bother.
577 	 */
578 	if (strcmp(vfsname, MOUNT_MFS) == 0) {
579 		pthread_t pt;
580 		struct mountinfo mi;
581 		int i;
582 
583 		mi.mi_vfsname = vfsname;
584 		mi.mi_mountpath = mountpath;
585 		mi.mi_mntflags = mntflags;
586 		mi.mi_arg = arg;
587 		mi.mi_alen = alen;
588 
589 		if (pthread_create(&pt, NULL, mfs_mounter, &mi) == -1) {
590 			rv = errno;
591 			goto out;
592 		}
593 
594 		for (i = 0;i < 100000; i++) {
595 			struct statvfs svfsb;
596 
597 			rv = rump_sys_statvfs1(mountpath, &svfsb, ST_WAIT);
598 			if (rv == -1) {
599 				rv = errno;
600 				goto out;
601 			}
602 
603 			if (strcmp(svfsb.f_mntonname, mountpath) == 0 &&
604 			    strcmp(svfsb.f_fstypename, MOUNT_MFS) == 0) {
605 				break;
606 			}
607 			usleep(1);
608 		}
609 	} else {
610 		rv = rump_sys_mount(vfsname, mountpath, mntflags, arg, alen);
611 		if (rv) {
612 			rv = errno;
613 			goto out;
614 		}
615 	}
616 
617 	mounted = 1;
618 	rv = rump_pub_vfs_getmp(mountpath, &fs->ukfs_mp);
619 	if (rv) {
620 		goto out;
621 	}
622 
623 	if (regged) {
624 		fs->ukfs_devpath = strdup(devpath);
625 	}
626 	fs->ukfs_mountpath = strdup(mountpath);
627 	pthread_spin_init(&fs->ukfs_spin, PTHREAD_PROCESS_SHARED);
628 	fs->ukfs_devfd = devfd;
629 	fs->ukfs_part = part;
630 	assert(rv == 0);
631 
632 	curlwp = rump_pub_lwproc_curlwp();
633 	rump_pub_lwproc_newlwp(0);
634 	fs->ukfs_lwp = rump_pub_lwproc_curlwp();
635 	fs->ukfs_cwd = strdup("/");
636 	rump_pub_lwproc_switch(curlwp);
637 
638  out:
639 	if (rv) {
640 		if (fs) {
641 			free(fs);
642 			fs = NULL;
643 		}
644 		if (mounted)
645 			rump_sys_unmount(mountpath, MNT_FORCE);
646 		if (regged)
647 			rump_pub_etfs_remove(devpath);
648 		if (devfd != -1) {
649 			unlockdev(devfd, part);
650 			close(devfd);
651 		}
652 		ukfs_part_release(part);
653 		errno = rv;
654 	}
655 
656 	return fs;
657 }
658 
659 struct ukfs *
ukfs_mount(const char * vfsname,const char * devpath,const char * mountpath,int mntflags,void * arg,size_t alen)660 ukfs_mount(const char *vfsname, const char *devpath,
661 	const char *mountpath, int mntflags, void *arg, size_t alen)
662 {
663 
664 	return doukfsmount(vfsname, devpath, ukfs_part_na,
665 	    mountpath, mntflags, arg, alen);
666 }
667 
668 struct ukfs *
ukfs_mount_disk(const char * vfsname,const char * devpath,struct ukfs_part * part,const char * mountpath,int mntflags,void * arg,size_t alen)669 ukfs_mount_disk(const char *vfsname, const char *devpath,
670 	struct ukfs_part *part, const char *mountpath, int mntflags,
671 	void *arg, size_t alen)
672 {
673 
674 	return doukfsmount(vfsname, devpath, part,
675 	    mountpath, mntflags, arg, alen);
676 }
677 
678 int
ukfs_release(struct ukfs * fs,int flags)679 ukfs_release(struct ukfs *fs, int flags)
680 {
681 	struct lwp *curlwp = rump_pub_lwproc_curlwp();
682 
683 	/* get root lwp */
684 	rump_pub_lwproc_switch(fs->ukfs_lwp);
685 	rump_pub_lwproc_rfork(RUMP_RFCFDG);
686 
687 	if ((flags & UKFS_RELFLAG_NOUNMOUNT) == 0) {
688 		int rv, mntflag, error;
689 
690 		mntflag = 0;
691 		if (flags & UKFS_RELFLAG_FORCE)
692 			mntflag = MNT_FORCE;
693 
694 		rv = rump_sys_unmount(fs->ukfs_mountpath, mntflag);
695 		if (rv == -1) {
696 			error = errno;
697 			rump_pub_lwproc_releaselwp();
698 			if (curlwp)
699 				rump_pub_lwproc_switch(curlwp);
700 			errno = error;
701 			return -1;
702 		}
703 	}
704 
705 	if (fs->ukfs_devpath) {
706 		rump_pub_etfs_remove(fs->ukfs_devpath);
707 		free(fs->ukfs_devpath);
708 	}
709 	free(fs->ukfs_mountpath);
710 	free(fs->ukfs_cwd);
711 
712 	/* release this routine's lwp and ukfs base lwp */
713 	rump_pub_lwproc_releaselwp();
714 	rump_pub_lwproc_switch(fs->ukfs_lwp);
715 	rump_pub_lwproc_releaselwp();
716 
717 	pthread_spin_destroy(&fs->ukfs_spin);
718 	if (fs->ukfs_devfd != -1) {
719 		unlockdev(fs->ukfs_devfd, fs->ukfs_part);
720 		close(fs->ukfs_devfd);
721 	}
722 	ukfs_part_release(fs->ukfs_part);
723 	free(fs);
724 
725 	if (curlwp)
726 		rump_pub_lwproc_switch(curlwp);
727 
728 	return 0;
729 }
730 
731 void
ukfs_part_release(struct ukfs_part * part)732 ukfs_part_release(struct ukfs_part *part)
733 {
734 	int release;
735 
736 	if (part != ukfs_part_none && part != ukfs_part_na) {
737 		pthread_spin_lock(&part->part_lck);
738 		release = --part->part_refcount == 0;
739 		pthread_spin_unlock(&part->part_lck);
740 		if (release) {
741 			pthread_spin_destroy(&part->part_lck);
742 			free(part);
743 		}
744 	}
745 }
746 
747 #define STDCALL(ukfs, thecall)						\
748 	int rv = 0;							\
749 									\
750 	PRECALL();							\
751 	rv = thecall;							\
752 	POSTCALL();							\
753 	return rv;
754 
755 int
ukfs_opendir(struct ukfs * ukfs,const char * dirname,struct ukfs_dircookie ** c)756 ukfs_opendir(struct ukfs *ukfs, const char *dirname, struct ukfs_dircookie **c)
757 {
758 	struct vnode *vp;
759 	int rv;
760 
761 	PRECALL();
762 	rv = rump_pub_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
763 	    NULL, &vp, NULL);
764 	POSTCALL();
765 
766 	if (rv == 0) {
767 		RUMP_VOP_UNLOCK(vp);
768 	} else {
769 		errno = rv;
770 		rv = -1;
771 	}
772 
773 	/*LINTED*/
774 	*c = (struct ukfs_dircookie *)vp;
775 	return rv;
776 }
777 
778 static int
getmydents(struct vnode * vp,off_t * off,uint8_t * buf,size_t bufsize)779 getmydents(struct vnode *vp, off_t *off, uint8_t *buf, size_t bufsize)
780 {
781 	struct uio *uio;
782 	size_t resid;
783 	int rv, eofflag;
784 	struct kauth_cred *cred;
785 
786 	uio = rump_pub_uio_setup(buf, bufsize, *off, RUMPUIO_READ);
787 	cred = rump_pub_cred_create(0, 0, 0, NULL);
788 	rv = RUMP_VOP_READDIR(vp, uio, cred, &eofflag, NULL, NULL);
789 	rump_pub_cred_put(cred);
790 	RUMP_VOP_UNLOCK(vp);
791 	*off = rump_pub_uio_getoff(uio);
792 	resid = rump_pub_uio_free(uio);
793 
794 	if (rv) {
795 		errno = rv;
796 		return -1;
797 	}
798 
799 	/* LINTED: not totally correct return type, but follows syscall */
800 	return bufsize - resid;
801 }
802 
803 /*ARGSUSED*/
804 int
ukfs_getdents_cookie(struct ukfs * ukfs,struct ukfs_dircookie * c,off_t * off,uint8_t * buf,size_t bufsize)805 ukfs_getdents_cookie(struct ukfs *ukfs, struct ukfs_dircookie *c, off_t *off,
806 	uint8_t *buf, size_t bufsize)
807 {
808 	/*LINTED*/
809 	struct vnode *vp = (struct vnode *)c;
810 
811 	RUMP_VOP_LOCK(vp, RUMP_LK_SHARED);
812 	return getmydents(vp, off, buf, bufsize);
813 }
814 
815 int
ukfs_getdents(struct ukfs * ukfs,const char * dirname,off_t * off,uint8_t * buf,size_t bufsize)816 ukfs_getdents(struct ukfs *ukfs, const char *dirname, off_t *off,
817 	uint8_t *buf, size_t bufsize)
818 {
819 	struct vnode *vp;
820 	int rv;
821 
822 	PRECALL();
823 	rv = rump_pub_namei(RUMP_NAMEI_LOOKUP, RUMP_NAMEI_LOCKLEAF, dirname,
824 	    NULL, &vp, NULL);
825 	if (rv) {
826 		POSTCALL();
827 		errno = rv;
828 		return -1;
829 	}
830 
831 	rv = getmydents(vp, off, buf, bufsize);
832 	rump_pub_vp_rele(vp);
833 	POSTCALL();
834 	return rv;
835 }
836 
837 /*ARGSUSED*/
838 int
ukfs_closedir(struct ukfs * ukfs,struct ukfs_dircookie * c)839 ukfs_closedir(struct ukfs *ukfs, struct ukfs_dircookie *c)
840 {
841 
842 	/*LINTED*/
843 	rump_pub_vp_rele((struct vnode *)c);
844 	return 0;
845 }
846 
847 int
ukfs_open(struct ukfs * ukfs,const char * filename,int flags)848 ukfs_open(struct ukfs *ukfs, const char *filename, int flags)
849 {
850 	int fd;
851 
852 	PRECALL();
853 	fd = rump_sys_open(filename, flags, 0);
854 	POSTCALL();
855 	if (fd == -1)
856 		return -1;
857 
858 	return fd;
859 }
860 
861 ssize_t
ukfs_read(struct ukfs * ukfs,const char * filename,off_t off,uint8_t * buf,size_t bufsize)862 ukfs_read(struct ukfs *ukfs, const char *filename, off_t off,
863 	uint8_t *buf, size_t bufsize)
864 {
865 	int fd;
866 	ssize_t xfer = -1; /* XXXgcc */
867 
868 	PRECALL();
869 	fd = rump_sys_open(filename, RUMP_O_RDONLY, 0);
870 	if (fd == -1)
871 		goto out;
872 
873 	xfer = rump_sys_pread(fd, buf, bufsize, off);
874 	rump_sys_close(fd);
875 
876  out:
877 	POSTCALL();
878 	if (fd == -1) {
879 		return -1;
880 	}
881 	return xfer;
882 }
883 
884 /*ARGSUSED*/
885 ssize_t
ukfs_read_fd(struct ukfs * ukfs,int fd,off_t off,uint8_t * buf,size_t buflen)886 ukfs_read_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen)
887 {
888 
889 	return rump_sys_pread(fd, buf, buflen, off);
890 }
891 
892 ssize_t
ukfs_write(struct ukfs * ukfs,const char * filename,off_t off,uint8_t * buf,size_t bufsize)893 ukfs_write(struct ukfs *ukfs, const char *filename, off_t off,
894 	uint8_t *buf, size_t bufsize)
895 {
896 	int fd;
897 	ssize_t xfer = -1; /* XXXgcc */
898 
899 	PRECALL();
900 	fd = rump_sys_open(filename, RUMP_O_WRONLY, 0);
901 	if (fd == -1)
902 		goto out;
903 
904 	/* write and commit */
905 	xfer = rump_sys_pwrite(fd, buf, bufsize, off);
906 	if (xfer > 0)
907 		rump_sys_fsync(fd);
908 
909 	rump_sys_close(fd);
910 
911  out:
912 	POSTCALL();
913 	if (fd == -1) {
914 		return -1;
915 	}
916 	return xfer;
917 }
918 
919 /*ARGSUSED*/
920 ssize_t
ukfs_write_fd(struct ukfs * ukfs,int fd,off_t off,uint8_t * buf,size_t buflen,int dosync)921 ukfs_write_fd(struct ukfs *ukfs, int fd, off_t off, uint8_t *buf, size_t buflen,
922 	int dosync)
923 {
924 	ssize_t xfer;
925 
926 	xfer = rump_sys_pwrite(fd, buf, buflen, off);
927 	if (xfer > 0 && dosync)
928 		rump_sys_fsync(fd);
929 
930 	return xfer;
931 }
932 
933 /*ARGSUSED*/
934 int
ukfs_close(struct ukfs * ukfs,int fd)935 ukfs_close(struct ukfs *ukfs, int fd)
936 {
937 
938 	rump_sys_close(fd);
939 	return 0;
940 }
941 
942 int
ukfs_create(struct ukfs * ukfs,const char * filename,mode_t mode)943 ukfs_create(struct ukfs *ukfs, const char *filename, mode_t mode)
944 {
945 	int fd;
946 
947 	PRECALL();
948 	fd = rump_sys_open(filename, RUMP_O_WRONLY | RUMP_O_CREAT, mode);
949 	if (fd == -1)
950 		return -1;
951 	rump_sys_close(fd);
952 
953 	POSTCALL();
954 	return 0;
955 }
956 
957 int
ukfs_mknod(struct ukfs * ukfs,const char * path,mode_t mode,dev_t dev)958 ukfs_mknod(struct ukfs *ukfs, const char *path, mode_t mode, dev_t dev)
959 {
960 
961 	STDCALL(ukfs, rump_sys_mknod(path, mode, dev));
962 }
963 
964 int
ukfs_mkfifo(struct ukfs * ukfs,const char * path,mode_t mode)965 ukfs_mkfifo(struct ukfs *ukfs, const char *path, mode_t mode)
966 {
967 
968 	STDCALL(ukfs, rump_sys_mkfifo(path, mode));
969 }
970 
971 int
ukfs_mkdir(struct ukfs * ukfs,const char * filename,mode_t mode)972 ukfs_mkdir(struct ukfs *ukfs, const char *filename, mode_t mode)
973 {
974 
975 	STDCALL(ukfs, rump_sys_mkdir(filename, mode));
976 }
977 
978 int
ukfs_remove(struct ukfs * ukfs,const char * filename)979 ukfs_remove(struct ukfs *ukfs, const char *filename)
980 {
981 
982 	STDCALL(ukfs, rump_sys_unlink(filename));
983 }
984 
985 int
ukfs_rmdir(struct ukfs * ukfs,const char * filename)986 ukfs_rmdir(struct ukfs *ukfs, const char *filename)
987 {
988 
989 	STDCALL(ukfs, rump_sys_rmdir(filename));
990 }
991 
992 int
ukfs_link(struct ukfs * ukfs,const char * filename,const char * f_create)993 ukfs_link(struct ukfs *ukfs, const char *filename, const char *f_create)
994 {
995 
996 	STDCALL(ukfs, rump_sys_link(filename, f_create));
997 }
998 
999 int
ukfs_symlink(struct ukfs * ukfs,const char * filename,const char * linkname)1000 ukfs_symlink(struct ukfs *ukfs, const char *filename, const char *linkname)
1001 {
1002 
1003 	STDCALL(ukfs, rump_sys_symlink(filename, linkname));
1004 }
1005 
1006 ssize_t
ukfs_readlink(struct ukfs * ukfs,const char * filename,char * linkbuf,size_t buflen)1007 ukfs_readlink(struct ukfs *ukfs, const char *filename,
1008 	char *linkbuf, size_t buflen)
1009 {
1010 	ssize_t rv;
1011 
1012 	PRECALL();
1013 	rv = rump_sys_readlink(filename, linkbuf, buflen);
1014 	POSTCALL();
1015 	return rv;
1016 }
1017 
1018 int
ukfs_rename(struct ukfs * ukfs,const char * from,const char * to)1019 ukfs_rename(struct ukfs *ukfs, const char *from, const char *to)
1020 {
1021 
1022 	STDCALL(ukfs, rump_sys_rename(from, to));
1023 }
1024 
1025 int
ukfs_chdir(struct ukfs * ukfs,const char * path)1026 ukfs_chdir(struct ukfs *ukfs, const char *path)
1027 {
1028 	char *newpath, *oldpath;
1029 	int rv;
1030 
1031 	PRECALL();
1032 	rv = rump_sys_chdir(path);
1033 	if (rv == -1)
1034 		goto out;
1035 
1036 	newpath = malloc(MAXPATHLEN);
1037 	if (rump_sys___getcwd(newpath, MAXPATHLEN) == -1) {
1038 		goto out;
1039 	}
1040 
1041 	pthread_spin_lock(&ukfs->ukfs_spin);
1042 	oldpath = ukfs->ukfs_cwd;
1043 	ukfs->ukfs_cwd = newpath;
1044 	pthread_spin_unlock(&ukfs->ukfs_spin);
1045 	free(oldpath);
1046 
1047  out:
1048 	POSTCALL();
1049 	return rv;
1050 }
1051 
1052 int
ukfs_stat(struct ukfs * ukfs,const char * filename,struct stat * file_stat)1053 ukfs_stat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
1054 {
1055 	int rv;
1056 
1057 	PRECALL();
1058 	rv = rump_sys_stat(filename, file_stat);
1059 	POSTCALL();
1060 
1061 	return rv;
1062 }
1063 
1064 int
ukfs_lstat(struct ukfs * ukfs,const char * filename,struct stat * file_stat)1065 ukfs_lstat(struct ukfs *ukfs, const char *filename, struct stat *file_stat)
1066 {
1067 	int rv;
1068 
1069 	PRECALL();
1070 	rv = rump_sys_lstat(filename, file_stat);
1071 	POSTCALL();
1072 
1073 	return rv;
1074 }
1075 
1076 int
ukfs_chmod(struct ukfs * ukfs,const char * filename,mode_t mode)1077 ukfs_chmod(struct ukfs *ukfs, const char *filename, mode_t mode)
1078 {
1079 
1080 	STDCALL(ukfs, rump_sys_chmod(filename, mode));
1081 }
1082 
1083 int
ukfs_lchmod(struct ukfs * ukfs,const char * filename,mode_t mode)1084 ukfs_lchmod(struct ukfs *ukfs, const char *filename, mode_t mode)
1085 {
1086 
1087 	STDCALL(ukfs, rump_sys_lchmod(filename, mode));
1088 }
1089 
1090 int
ukfs_chown(struct ukfs * ukfs,const char * filename,uid_t uid,gid_t gid)1091 ukfs_chown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
1092 {
1093 
1094 	STDCALL(ukfs, rump_sys_chown(filename, uid, gid));
1095 }
1096 
1097 int
ukfs_lchown(struct ukfs * ukfs,const char * filename,uid_t uid,gid_t gid)1098 ukfs_lchown(struct ukfs *ukfs, const char *filename, uid_t uid, gid_t gid)
1099 {
1100 
1101 	STDCALL(ukfs, rump_sys_lchown(filename, uid, gid));
1102 }
1103 
1104 int
ukfs_chflags(struct ukfs * ukfs,const char * filename,u_long flags)1105 ukfs_chflags(struct ukfs *ukfs, const char *filename, u_long flags)
1106 {
1107 
1108 	STDCALL(ukfs, rump_sys_chflags(filename, flags));
1109 }
1110 
1111 int
ukfs_lchflags(struct ukfs * ukfs,const char * filename,u_long flags)1112 ukfs_lchflags(struct ukfs *ukfs, const char *filename, u_long flags)
1113 {
1114 
1115 	STDCALL(ukfs, rump_sys_lchflags(filename, flags));
1116 }
1117 
1118 int
ukfs_utimes(struct ukfs * ukfs,const char * filename,const struct timeval * tptr)1119 ukfs_utimes(struct ukfs *ukfs, const char *filename, const struct timeval *tptr)
1120 {
1121 
1122 	STDCALL(ukfs, rump_sys_utimes(filename, tptr));
1123 }
1124 
1125 int
ukfs_lutimes(struct ukfs * ukfs,const char * filename,const struct timeval * tptr)1126 ukfs_lutimes(struct ukfs *ukfs, const char *filename,
1127 	      const struct timeval *tptr)
1128 {
1129 
1130 	STDCALL(ukfs, rump_sys_lutimes(filename, tptr));
1131 }
1132 
1133 /*
1134  * Dynamic module support
1135  */
1136 
1137 /* load one library */
1138 
1139 /*
1140  * XXX: the dlerror stuff isn't really threadsafe, but then again I
1141  * can't protect against other threads calling dl*() outside of ukfs,
1142  * so just live with it being flimsy
1143  */
1144 int
ukfs_modload(const char * fname)1145 ukfs_modload(const char *fname)
1146 {
1147 	void *handle;
1148 	const struct modinfo *const *mi_start, *const *mi_end;
1149 	int error;
1150 
1151 	handle = dlopen(fname, RTLD_LAZY|RTLD_GLOBAL);
1152 	if (handle == NULL) {
1153 		const char *dlmsg = dlerror();
1154 		if (strstr(dlmsg, "Undefined symbol"))
1155 			return 0;
1156 		warnx("dlopen %s failed: %s", fname, dlmsg);
1157 		/* XXXerrno */
1158 		return -1;
1159 	}
1160 
1161 	mi_start = dlsym(handle, "__start_link_set_modules");
1162 	mi_end = dlsym(handle, "__stop_link_set_modules");
1163 	if (mi_start && mi_end) {
1164 		error = rump_pub_module_init(mi_start,
1165 		    (size_t)(mi_end-mi_start));
1166 		if (error)
1167 			goto errclose;
1168 		return 1;
1169 	}
1170 	error = EINVAL;
1171 
1172  errclose:
1173 	dlclose(handle);
1174 	errno = error;
1175 	return -1;
1176 }
1177 
1178 struct loadfail {
1179 	char *pname;
1180 
1181 	LIST_ENTRY(loadfail) entries;
1182 };
1183 
1184 #define RUMPFSMOD_PREFIX "librumpfs_"
1185 #define RUMPFSMOD_SUFFIX ".so"
1186 
1187 int
ukfs_modload_dir(const char * dir)1188 ukfs_modload_dir(const char *dir)
1189 {
1190 	char nbuf[MAXPATHLEN+1], *p;
1191 	struct dirent entry, *result;
1192 	DIR *libdir;
1193 	struct loadfail *lf, *nlf;
1194 	int error, nloaded = 0, redo;
1195 	LIST_HEAD(, loadfail) lfs;
1196 
1197 	libdir = opendir(dir);
1198 	if (libdir == NULL)
1199 		return -1;
1200 
1201 	LIST_INIT(&lfs);
1202 	for (;;) {
1203 		if ((error = readdir_r(libdir, &entry, &result)) != 0)
1204 			break;
1205 		if (!result)
1206 			break;
1207 		if (strncmp(result->d_name, RUMPFSMOD_PREFIX,
1208 		    strlen(RUMPFSMOD_PREFIX)) != 0)
1209 			continue;
1210 		if (((p = strstr(result->d_name, RUMPFSMOD_SUFFIX)) == NULL)
1211 		    || strlen(p) != strlen(RUMPFSMOD_SUFFIX))
1212 			continue;
1213 		strlcpy(nbuf, dir, sizeof(nbuf));
1214 		strlcat(nbuf, "/", sizeof(nbuf));
1215 		strlcat(nbuf, result->d_name, sizeof(nbuf));
1216 		switch (ukfs_modload(nbuf)) {
1217 		case 0:
1218 			lf = malloc(sizeof(*lf));
1219 			if (lf == NULL) {
1220 				error = ENOMEM;
1221 				break;
1222 			}
1223 			lf->pname = strdup(nbuf);
1224 			if (lf->pname == NULL) {
1225 				free(lf);
1226 				error = ENOMEM;
1227 				break;
1228 			}
1229 			LIST_INSERT_HEAD(&lfs, lf, entries);
1230 			break;
1231 		case 1:
1232 			nloaded++;
1233 			break;
1234 		default:
1235 			/* ignore errors */
1236 			break;
1237 		}
1238 	}
1239 	closedir(libdir);
1240 	if (error && nloaded != 0)
1241 		error = 0;
1242 
1243 	/*
1244 	 * El-cheapo dependency calculator.  Just try to load the
1245 	 * modules n times in a loop
1246 	 */
1247 	for (redo = 1; redo;) {
1248 		redo = 0;
1249 		nlf = LIST_FIRST(&lfs);
1250 		while ((lf = nlf) != NULL) {
1251 			nlf = LIST_NEXT(lf, entries);
1252 			if (ukfs_modload(lf->pname) == 1) {
1253 				nloaded++;
1254 				redo = 1;
1255 				LIST_REMOVE(lf, entries);
1256 				free(lf->pname);
1257 				free(lf);
1258 			}
1259 		}
1260 	}
1261 
1262 	while ((lf = LIST_FIRST(&lfs)) != NULL) {
1263 		LIST_REMOVE(lf, entries);
1264 		free(lf->pname);
1265 		free(lf);
1266 	}
1267 
1268 	if (error && nloaded == 0) {
1269 		errno = error;
1270 		return -1;
1271 	}
1272 
1273 	return nloaded;
1274 }
1275 
1276 /* XXX: this code uses definitions from NetBSD, needs rumpdefs */
1277 ssize_t
ukfs_vfstypes(char * buf,size_t buflen)1278 ukfs_vfstypes(char *buf, size_t buflen)
1279 {
1280 	int mib[3];
1281 	struct sysctlnode q, ans[128];
1282 	size_t alen;
1283 	int i;
1284 
1285 	mib[0] = CTL_VFS;
1286 	mib[1] = VFS_GENERIC;
1287 	mib[2] = CTL_QUERY;
1288 	alen = sizeof(ans);
1289 
1290 	memset(&q, 0, sizeof(q));
1291 	q.sysctl_flags = SYSCTL_VERSION;
1292 
1293 	if (rump_sys___sysctl(mib, 3, ans, &alen, &q, sizeof(q)) == -1) {
1294 		return -1;
1295 	}
1296 
1297 	for (i = 0; i < alen/sizeof(ans[0]); i++)
1298 		if (strcmp("fstypes", ans[i].sysctl_name) == 0)
1299 			break;
1300 	if (i == alen/sizeof(ans[0])) {
1301 		errno = ENXIO;
1302 		return -1;
1303 	}
1304 
1305 	mib[0] = CTL_VFS;
1306 	mib[1] = VFS_GENERIC;
1307 	mib[2] = ans[i].sysctl_num;
1308 
1309 	if (rump_sys___sysctl(mib, 3, buf, &buflen, NULL, 0) == -1) {
1310 		return -1;
1311 	}
1312 
1313 	return buflen;
1314 }
1315 
1316 /*
1317  * Utilities
1318  */
1319 static int
builddirs(const char * pathname,mode_t mode,int (* mkdirfn)(struct ukfs *,const char *,mode_t),struct ukfs * fs)1320 builddirs(const char *pathname, mode_t mode,
1321 	int (*mkdirfn)(struct ukfs *, const char *, mode_t), struct ukfs *fs)
1322 {
1323 	char *f1, *f2;
1324 	int rv;
1325 	mode_t mask;
1326 	bool end;
1327 
1328 	/*ukfs_umask((mask = ukfs_umask(0)));*/
1329 	umask((mask = umask(0)));
1330 
1331 	f1 = f2 = strdup(pathname);
1332 	if (f1 == NULL) {
1333 		errno = ENOMEM;
1334 		return -1;
1335 	}
1336 
1337 	end = false;
1338 	for (;;) {
1339 		/* find next component */
1340 		f2 += strspn(f2, "/");
1341 		f2 += strcspn(f2, "/");
1342 		if (*f2 == '\0')
1343 			end = true;
1344 		else
1345 			*f2 = '\0';
1346 
1347 		rv = mkdirfn(fs, f1, mode & ~mask);
1348 		if (errno == EEXIST)
1349 			rv = 0;
1350 
1351 		if (rv == -1 || *f2 != '\0' || end)
1352 			break;
1353 
1354 		*f2 = '/';
1355 	}
1356 
1357 	free(f1);
1358 
1359 	return rv;
1360 }
1361 
1362 int
ukfs_util_builddirs(struct ukfs * ukfs,const char * pathname,mode_t mode)1363 ukfs_util_builddirs(struct ukfs *ukfs, const char *pathname, mode_t mode)
1364 {
1365 
1366 	return builddirs(pathname, mode, ukfs_mkdir, ukfs);
1367 }
1368