xref: /minix3/lib/libpuffs/null.c (revision f14fb602092e015ff630df58e17c2a9cd57d29b3)
1 /*	$NetBSD: null.c,v 1.25 2008/08/12 19:44:39 pooka Exp $	*/
2 
3 /*
4  * Copyright (c) 2007  Antti Kantee.  All Rights Reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 #if !defined(lint)
30 __RCSID("$NetBSD: null.c,v 1.25 2008/08/12 19:44:39 pooka Exp $");
31 #endif /* !lint */
32 
33 /*
34  * A "nullfs" using puffs, i.e. maps one location in the hierarchy
35  * to another using standard system calls.
36  */
37 
38 #include <sys/types.h>
39 #include <sys/time.h>
40 
41 #include <assert.h>
42 #include <dirent.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <unistd.h>
48 #include <utime.h>
49 
50 #include "puffs.h"
51 
52 
53 PUFFSOP_PROTOS(puffs_null)
54 
55 /*
56  * set attributes to what is specified.  XXX: no rollback in case of failure
57  */
58 static int
59 processvattr(const char *path, const struct vattr *va, int regular)
60 {
61 	struct utimbuf tbuf;
62 
63 	/* XXX: -1 == PUFFS_VNOVAL, but shouldn't trust that */
64 	if (va->va_uid != (unsigned)-1 || va->va_gid != (unsigned)-1)
65                 /* FIXME: lchown */
66 		if (chown(path, va->va_uid, va->va_gid) == -1)
67 			return errno;
68 
69 #ifndef __minix
70 	if (va->va_mode != (unsigned)PUFFS_VNOVAL)
71 #endif
72                 /* FIXME: lchmod */
73 		if (chmod(path, va->va_mode) == -1)
74 			return errno;
75 
76 	/* sloppy */
77 	if (va->va_atime.tv_sec != (unsigned)PUFFS_VNOVAL
78 	    || va->va_mtime.tv_sec != (unsigned)PUFFS_VNOVAL) {
79 		/* FIXME: nsec too */
80 		tbuf.actime = va->va_atime.tv_sec;
81 		tbuf.modtime = va->va_mtime.tv_sec;
82 
83                 /* FIXME: lutimes */
84 		if (utime(path, &tbuf) == -1)
85 			return errno;
86 	}
87 
88 	if (regular && va->va_size != (u_quad_t)PUFFS_VNOVAL)
89 		if (truncate(path, (off_t)va->va_size) == -1)
90 			return errno;
91 
92 	return 0;
93 }
94 
95 /*
96  * Kludge to open files which aren't writable *any longer*.  This kinda
97  * works because the vfs layer does validation checks based on the file's
98  * permissions to allow writable opening before opening them.  However,
99  * the problem arises if we want to create a file, write to it (cache),
100  * adjust permissions and then flush the file.
101  */
102 static int
103 writeableopen(const char *path)
104 {
105 	struct stat sb;
106 	mode_t origmode;
107 	int sverr = 0;
108 	int fd;
109 
110 	fd = open(path, O_WRONLY);
111 	if (fd == -1) {
112 		if (errno == EACCES) {
113 			if (stat(path, &sb) == -1)
114 				return -1;
115 			origmode = sb.st_mode & ALLPERMS;
116 
117 			if (chmod(path, 0200) == -1)
118 				return -1;
119 
120 			fd = open(path, O_WRONLY);
121 			if (fd == -1)
122 				sverr = errno;
123 
124 			chmod(path, origmode);
125 			if (sverr)
126 				errno = sverr;
127 		} else
128 			return -1;
129 	}
130 
131 	return fd;
132 }
133 
134 /*ARGSUSED*/
135 static void *
136 inodecmp(struct puffs_usermount *pu, struct puffs_node *pn, void *arg)
137 {
138 	ino_t *cmpino = arg;
139 
140 	if (pn->pn_va.va_fileid == *cmpino)
141 		return pn;
142 	return NULL;
143 }
144 
145 static int
146 makenode(struct puffs_usermount *pu, struct puffs_newinfo *pni,
147 	const struct puffs_cn *pcn, const struct vattr *va, int regular)
148 {
149 	struct puffs_node *pn;
150 	struct stat sb;
151 	int rv;
152 
153 	if ((rv = processvattr(PCNPATH(pcn), va, regular)) != 0)
154 		return rv;
155 
156 	pn = puffs_pn_new(pu, NULL);
157 	if (!pn)
158 		return ENOMEM;
159 	puffs_setvattr(&pn->pn_va, va);
160 
161 	if (lstat(PCNPATH(pcn), &sb) == -1)
162 		return errno;
163 	puffs_stat2vattr(&pn->pn_va, &sb);
164 
165 	puffs_newinfo_setcookie(pni, pn);
166 	return 0;
167 }
168 
169 /* This should be called first and overriden from the file system */
170 void
171 puffs_null_setops(struct puffs_ops *pops)
172 {
173 
174 	PUFFSOP_SET(pops, puffs_null, fs, statvfs);
175 	PUFFSOP_SETFSNOP(pops, unmount);
176 	PUFFSOP_SETFSNOP(pops, sync);
177 
178 	PUFFSOP_SET(pops, puffs_null, node, lookup);
179 	PUFFSOP_SET(pops, puffs_null, node, create);
180 	PUFFSOP_SET(pops, puffs_null, node, mknod);
181 	PUFFSOP_SET(pops, puffs_null, node, getattr);
182 	PUFFSOP_SET(pops, puffs_null, node, setattr);
183 	PUFFSOP_SET(pops, puffs_null, node, fsync);
184 	PUFFSOP_SET(pops, puffs_null, node, remove);
185 	PUFFSOP_SET(pops, puffs_null, node, link);
186 	PUFFSOP_SET(pops, puffs_null, node, rename);
187 	PUFFSOP_SET(pops, puffs_null, node, mkdir);
188 	PUFFSOP_SET(pops, puffs_null, node, rmdir);
189 	PUFFSOP_SET(pops, puffs_null, node, symlink);
190 	PUFFSOP_SET(pops, puffs_null, node, readlink);
191 	PUFFSOP_SET(pops, puffs_null, node, readdir);
192 	PUFFSOP_SET(pops, puffs_null, node, read);
193 	PUFFSOP_SET(pops, puffs_null, node, write);
194 	PUFFSOP_SET(pops, puffs_genfs, node, reclaim);
195 }
196 
197 /*ARGSUSED*/
198 int
199 puffs_null_fs_statvfs(struct puffs_usermount *pu, struct statvfs *svfsb)
200 {
201 
202 	if (statvfs(PNPATH(puffs_getroot(pu)), svfsb) == -1)
203 		return errno;
204 
205 	return 0;
206 }
207 
208 int
209 puffs_null_node_lookup(struct puffs_usermount *pu, puffs_cookie_t opc,
210 	struct puffs_newinfo *pni, const struct puffs_cn *pcn)
211 {
212 	struct puffs_node *pn = opc, *pn_res;
213 	struct stat sb;
214 	int rv;
215 
216 	assert(pn->pn_va.va_type == VDIR);
217 
218 	/*
219 	 * Note to whoever is copypasting this: you must first check
220 	 * if the node is there and only then do nodewalk.  Alternatively
221 	 * you could make sure that you don't return unlinked/rmdir'd
222 	 * nodes in some other fashion
223 	 */
224 	rv = lstat(PCNPATH(pcn), &sb);
225 	if (rv)
226 		return errno;
227 
228 	/* XXX2: nodewalk is a bit too slow here */
229 	pn_res = puffs_pn_nodewalk(pu, inodecmp, &sb.st_ino);
230 
231 	if (pn_res == NULL) {
232 		pn_res = puffs_pn_new(pu, NULL);
233 		if (pn_res == NULL)
234 			return ENOMEM;
235 		puffs_stat2vattr(&pn_res->pn_va, &sb);
236 	}
237 
238 	puffs_newinfo_setcookie(pni, pn_res);
239 	puffs_newinfo_setvtype(pni, pn_res->pn_va.va_type);
240 	puffs_newinfo_setsize(pni, (voff_t)pn_res->pn_va.va_size);
241 	puffs_newinfo_setrdev(pni, pn_res->pn_va.va_rdev);
242 
243 	return 0;
244 }
245 
246 /*ARGSUSED*/
247 int
248 puffs_null_node_create(struct puffs_usermount *pu, puffs_cookie_t opc,
249 	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
250 	const struct vattr *va)
251 {
252 	int fd, rv;
253 
254 	fd = open(PCNPATH(pcn), O_RDWR | O_CREAT | O_TRUNC);
255 	if (fd == -1)
256 		return errno;
257 	close(fd);
258 
259 	rv = makenode(pu, pni, pcn, va, 1);
260 	if (rv)
261 		unlink(PCNPATH(pcn));
262 	return rv;
263 }
264 
265 /*ARGSUSED*/
266 int
267 puffs_null_node_mknod(struct puffs_usermount *pu, puffs_cookie_t opc,
268 	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
269 	const struct vattr *va)
270 {
271 	mode_t mode;
272 	int rv;
273 
274 	mode = puffs_addvtype2mode(va->va_mode, va->va_type);
275 	if (mknod(PCNPATH(pcn), mode, va->va_rdev) == -1)
276 		return errno;
277 
278 	rv = makenode(pu, pni, pcn, va, 0);
279 	if (rv)
280 		unlink(PCNPATH(pcn));
281 	return rv;
282 }
283 
284 /*ARGSUSED*/
285 int
286 puffs_null_node_getattr(struct puffs_usermount *pu, puffs_cookie_t opc,
287 	struct vattr *va, const struct puffs_cred *pcred)
288 {
289 	struct puffs_node *pn = opc;
290 	struct stat sb;
291 
292 	if (lstat(PNPATH(pn), &sb) == -1)
293 		return errno;
294 	puffs_stat2vattr(va, &sb);
295 
296 	return 0;
297 }
298 
299 /*ARGSUSED*/
300 int
301 puffs_null_node_setattr(struct puffs_usermount *pu, puffs_cookie_t opc,
302 	const struct vattr *va, const struct puffs_cred *pcred)
303 {
304 	struct puffs_node *pn = opc;
305 	int rv;
306 
307 	rv = processvattr(PNPATH(pn), va, pn->pn_va.va_type == VREG);
308 	if (rv)
309 		return rv;
310 
311 	puffs_setvattr(&pn->pn_va, va);
312 
313 	return 0;
314 }
315 
316 /*ARGSUSED*/
317 int
318 puffs_null_node_fsync(struct puffs_usermount *pu, puffs_cookie_t opc,
319 	const struct puffs_cred *pcred, int how,
320 	off_t offlo, off_t offhi)
321 {
322 /* FIXME: implement me. */
323 #if 0
324 	struct puffs_node *pn = opc;
325 	int fd, rv;
326 	int fflags;
327 
328 	rv = 0;
329 	fd = writeableopen(PNPATH(pn));
330 	if (fd == -1)
331 		return errno;
332 
333 	if (how & PUFFS_FSYNC_DATAONLY)
334 		fflags = FDATASYNC;
335 	else
336 		fflags = FFILESYNC;
337 	if (how & PUFFS_FSYNC_CACHE)
338 		fflags |= FDISKSYNC;
339 
340 	if (fsync_range(fd, fflags, offlo, offhi - offlo) == -1)
341 		rv = errno;
342 
343 	close(fd);
344 
345 	return rv;
346 #endif
347 	return 0;
348 }
349 
350 /*ARGSUSED*/
351 int
352 puffs_null_node_remove(struct puffs_usermount *pu, puffs_cookie_t opc,
353 	puffs_cookie_t targ, const struct puffs_cn *pcn)
354 {
355 	struct puffs_node *pn_targ = targ;
356 
357 	if (unlink(PCNPATH(pcn)) == -1)
358 		return errno;
359 	puffs_pn_remove(pn_targ);
360 
361 	return 0;
362 }
363 
364 /*ARGSUSED*/
365 int
366 puffs_null_node_link(struct puffs_usermount *pu, puffs_cookie_t opc,
367 	puffs_cookie_t targ, const struct puffs_cn *pcn)
368 {
369 	struct puffs_node *pn_targ = targ;
370 
371 	if (link(PNPATH(pn_targ), PCNPATH(pcn)) == -1)
372 		return errno;
373 
374 	return 0;
375 }
376 
377 /*ARGSUSED*/
378 int
379 puffs_null_node_rename(struct puffs_usermount *pu, puffs_cookie_t opc,
380 	puffs_cookie_t src, const struct puffs_cn *pcn_src,
381 	puffs_cookie_t targ_dir, puffs_cookie_t targ,
382 	const struct puffs_cn *pcn_targ)
383 {
384 	struct puffs_node *pn_targ = targ;
385 
386 	if (rename(PCNPATH(pcn_src), PCNPATH(pcn_targ)) == -1)
387 		return errno;
388 
389 	if (pn_targ)
390 		puffs_pn_remove(pn_targ);
391 
392 	return 0;
393 }
394 
395 /*ARGSUSED*/
396 int
397 puffs_null_node_mkdir(struct puffs_usermount *pu, puffs_cookie_t opc,
398 	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
399 	const struct vattr *va)
400 {
401 	int rv;
402 
403 	if (mkdir(PCNPATH(pcn), va->va_mode) == -1)
404 		return errno;
405 
406 	rv = makenode(pu, pni, pcn, va, 0);
407 	if (rv)
408 		rmdir(PCNPATH(pcn));
409 	return rv;
410 }
411 
412 /*ARGSUSED*/
413 int
414 puffs_null_node_rmdir(struct puffs_usermount *pu, puffs_cookie_t opc,
415 	puffs_cookie_t targ, const struct puffs_cn *pcn)
416 {
417 	struct puffs_node *pn_targ = targ;
418 
419 	if (rmdir(PNPATH(pn_targ)) == -1)
420 		return errno;
421 	puffs_pn_remove(pn_targ);
422 
423 	return 0;
424 }
425 
426 /*ARGSUSED*/
427 int
428 puffs_null_node_symlink(struct puffs_usermount *pu, puffs_cookie_t opc,
429 	struct puffs_newinfo *pni, const struct puffs_cn *pcn,
430 	const struct vattr *va, const char *linkname)
431 {
432 	int rv;
433 
434 	if (symlink(linkname, PCNPATH(pcn)) == -1)
435 		return errno;
436 
437 	rv = makenode(pu, pni, pcn, va, 0);
438 	if (rv)
439 		unlink(PCNPATH(pcn));
440 	return rv;
441 }
442 
443 /*ARGSUSED*/
444 int
445 puffs_null_node_readlink(struct puffs_usermount *pu, puffs_cookie_t opc,
446 	const struct puffs_cred *pcred, char *linkname, size_t *linklen)
447 {
448 	struct puffs_node *pn = opc;
449 	ssize_t rv;
450 
451 	rv = readlink(PNPATH(pn), linkname, *linklen);
452 	if (rv == -1)
453 		return errno;
454 
455 	*linklen = rv;
456 	return 0;
457 }
458 
459 /*ARGSUSED*/
460 int
461 puffs_null_node_readdir(struct puffs_usermount *pu, puffs_cookie_t opc,
462 	struct dirent *de, off_t *off, size_t *reslen,
463 	const struct puffs_cred *pcred, int *eofflag, off_t *cookies,
464 	size_t *ncookies)
465 {
466 	/* TODO: use original code since we have libc from NetBSD */
467 	struct puffs_node *pn = opc;
468 	struct dirent *entry;
469 	DIR *dp;
470 	off_t i;
471 	int rv;
472 
473 	dp = opendir(PNPATH(pn));
474 	if (dp == NULL)
475 		return errno;
476 
477 	rv = 0;
478 	i = *off;
479 
480 	/*
481 	 * XXX: need to do trickery here, telldir/seekdir would be nice, but
482 	 * then we'd need to keep state, which I'm too lazy to keep
483 	 */
484 	while (i--) {
485 		entry = readdir(dp);
486 		if (!entry) {
487 			*eofflag = 1;
488 			goto out;
489 		}
490 	}
491 
492 	for (;;) {
493                 /* FIXME: DIRENT_SIZE macro? For now do calculations here */
494 		int namelen;
495 		char* cp;
496 		int dirent_size;
497 
498                 entry = readdir(dp);
499 
500 		if (!entry) {
501 			*eofflag = 1;
502 			goto out;
503 		}
504 
505 		cp = memchr(entry->d_name, '\0', NAME_MAX);
506 		if (cp == NULL)
507 			namelen = NAME_MAX;
508 		else
509 			namelen = cp - (entry->d_name);
510 		dirent_size = _DIRENT_RECLEN(entry, namelen);
511 
512 		if (dirent_size > *reslen)
513 			goto out;
514 
515 		*de = *entry;
516 		strncpy(de->d_name, entry->d_name, namelen);
517 		*reslen -= dirent_size;
518 
519 		de = _DIRENT_NEXT(de);
520 		(*off)++;
521 	}
522 
523  out:
524 	closedir(dp);
525 	return 0;
526 }
527 
528 /*ARGSUSED*/
529 int
530 puffs_null_node_read(struct puffs_usermount *pu, puffs_cookie_t opc,
531 	uint8_t *buf, off_t offset, size_t *buflen,
532 	const struct puffs_cred *pcred, int ioflag)
533 {
534 	struct puffs_node *pn = opc;
535 	ssize_t n;
536 	off_t off;
537 	int fd, rv;
538 
539 	rv = 0;
540 	fd = open(PNPATH(pn), O_RDONLY);
541 	if (fd == -1)
542 		return errno;
543 	off = lseek(fd, offset, SEEK_SET);
544 	if (off == -1) {
545 		rv = errno;
546 		goto out;
547 	}
548 
549 	n = read(fd, buf, *buflen);
550 	if (n == -1)
551 		rv = errno;
552 	else
553 		*buflen -= n;
554 
555  out:
556 	close(fd);
557 	return rv;
558 }
559 
560 /*ARGSUSED*/
561 int
562 puffs_null_node_write(struct puffs_usermount *pu, puffs_cookie_t opc,
563 	uint8_t *buf, off_t offset, size_t *buflen,
564 	const struct puffs_cred *pcred, int ioflag)
565 {
566 	struct puffs_node *pn = opc;
567 	ssize_t n;
568 	off_t off;
569 	int fd, rv;
570 
571 	rv = 0;
572 	fd = writeableopen(PNPATH(pn));
573 	if (fd == -1)
574 		return errno;
575 
576 	off = lseek(fd, offset, SEEK_SET);
577 	if (off == -1) {
578 		rv = errno;
579 		goto out;
580 	}
581 
582 	n = write(fd, buf, *buflen);
583 	if (n == -1)
584 		rv = errno;
585 	else
586 		*buflen -= n;
587 
588  out:
589 	close(fd);
590 	return rv;
591 }
592