1/*	$NetBSD: chfs_subr.c,v 1.1 2011/11/24 15:51:31 ahoka Exp $	*/
2
3/*-
4 * Copyright (c) 2010 Department of Software Engineering,
5 *		      University of Szeged, Hungary
6 * Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu>
7 * Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
8 * All rights reserved.
9 *
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by the Department of Software Engineering, University of Szeged, Hungary
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35/*
36 * Efficient memory file system supporting functions.
37 */
38
39#include <sys/cdefs.h>
40
41#include <sys/param.h>
42#include <sys/dirent.h>
43#include <sys/event.h>
44#include <sys/kmem.h>
45#include <sys/mount.h>
46#include <sys/namei.h>
47#include <sys/time.h>
48#include <sys/stat.h>
49#include <sys/systm.h>
50#include <sys/swap.h>
51#include <sys/vnode.h>
52#include <sys/kauth.h>
53#include <sys/proc.h>
54#include <sys/atomic.h>
55
56#include <uvm/uvm.h>
57
58#include <miscfs/specfs/specdev.h>
59#include "chfs.h"
60//#include <fs/chfs/chfs_vnops.h>
61//#include </root/xipffs/netbsd.chfs/chfs.h>
62
63/* --------------------------------------------------------------------- */
64
65/*
66 * Returns information about the number of available memory pages,
67 * including physical and virtual ones.
68 *
69 * If 'total' is true, the value returned is the total amount of memory
70 * pages configured for the system (either in use or free).
71 * If it is FALSE, the value returned is the amount of free memory pages.
72 *
73 * Remember to remove DUMMYFS_PAGES_RESERVED from the returned value to avoid
74 * excessive memory usage.
75 *
76 */
77size_t
78chfs_mem_info(bool total)
79{
80	size_t size;
81
82	size = 0;
83	size += uvmexp.swpgavail;
84	if (!total) {
85		size -= uvmexp.swpgonly;
86	}
87	size += uvmexp.free;
88	size += uvmexp.filepages;
89	if (size > uvmexp.wired) {
90		size -= uvmexp.wired;
91	} else {
92		size = 0;
93	}
94
95	return size;
96}
97
98
99/* --------------------------------------------------------------------- */
100
101/*
102 * Looks for a directory entry in the directory represented by node.
103 * 'cnp' describes the name of the entry to look for.  Note that the .
104 * and .. components are not allowed as they do not physically exist
105 * within directories.
106 *
107 * Returns a pointer to the entry when found, otherwise NULL.
108 */
109struct chfs_dirent *
110chfs_dir_lookup(struct chfs_inode *ip, struct componentname *cnp)
111{
112	bool found;
113	struct chfs_dirent *fd;
114	dbg("dir_lookup()\n");
115
116	KASSERT(IMPLIES(cnp->cn_namelen == 1, cnp->cn_nameptr[0] != '.'));
117	KASSERT(IMPLIES(cnp->cn_namelen == 2, !(cnp->cn_nameptr[0] == '.' &&
118		    cnp->cn_nameptr[1] == '.')));
119	//CHFS_VALIDATE_DIR(node);
120
121	//node->chn_status |= CHFS_NODE_ACCESSED;
122
123	found = false;
124//	fd = ip->dents;
125//	while(fd) {
126	TAILQ_FOREACH(fd, &ip->dents, fds) {
127		KASSERT(cnp->cn_namelen < 0xffff);
128		if (fd->vno == 0)
129			continue;
130		/*dbg("dirent dump:\n");
131		  dbg(" ->vno:     %d\n", fd->vno);
132		  dbg(" ->version: %ld\n", fd->version);
133		  dbg(" ->nhash:   0x%x\n", fd->nhash);
134		  dbg(" ->nsize:   %d\n", fd->nsize);
135		  dbg(" ->name:    %s\n", fd->name);
136		  dbg(" ->type:    %d\n", fd->type);*/
137		if (fd->nsize == (uint16_t)cnp->cn_namelen &&
138		    memcmp(fd->name, cnp->cn_nameptr, fd->nsize) == 0) {
139			found = true;
140			break;
141		}
142//		fd = fd->next;
143	}
144
145	return found ? fd : NULL;
146}
147
148/* --------------------------------------------------------------------- */
149
150int
151chfs_filldir(struct uio* uio, ino_t ino, const char *name,
152    int namelen, enum vtype type)
153{
154	struct dirent dent;
155	int error;
156
157	memset(&dent, 0, sizeof(dent));
158
159	dent.d_fileno = ino;
160	switch (type) {
161	case VBLK:
162		dent.d_type = DT_BLK;
163		break;
164
165	case VCHR:
166		dent.d_type = DT_CHR;
167		break;
168
169	case VDIR:
170		dent.d_type = DT_DIR;
171		break;
172
173	case VFIFO:
174		dent.d_type = DT_FIFO;
175		break;
176
177	case VLNK:
178		dent.d_type = DT_LNK;
179		break;
180
181	case VREG:
182		dent.d_type = DT_REG;
183		break;
184
185	case VSOCK:
186		dent.d_type = DT_SOCK;
187		break;
188
189	default:
190		KASSERT(0);
191	}
192	dent.d_namlen = namelen;
193	(void)memcpy(dent.d_name, name, dent.d_namlen);
194	dent.d_reclen = _DIRENT_SIZE(&dent);
195
196	if (dent.d_reclen > uio->uio_resid) {
197		error = -1;
198	} else {
199		error = uiomove(&dent, dent.d_reclen, uio);
200	}
201
202	return error;
203}
204
205
206/* --------------------------------------------------------------------- */
207
208/*
209 * Change size of the given vnode.
210 * Caller should execute chfs_update on vp after a successful execution.
211 * The vnode must be locked on entry and remain locked on exit.
212 */
213int
214chfs_chsize(struct vnode *vp, u_quad_t size, kauth_cred_t cred)
215{
216	struct chfs_mount *chmp;
217	struct chfs_inode *ip;
218	struct buf *bp;
219	int blknum, append;
220	int error = 0;
221	char *buf = NULL;
222	struct chfs_full_dnode *fd;
223
224	ip = VTOI(vp);
225	chmp = ip->chmp;
226
227	dbg("chfs_chsize\n");
228
229	switch (vp->v_type) {
230	case VDIR:
231		return EISDIR;
232	case VLNK:
233	case VREG:
234		if (vp->v_mount->mnt_flag & MNT_RDONLY)
235			return EROFS;
236		break;
237	case VBLK:
238	case VCHR:
239	case VFIFO:
240		return 0;
241	default:
242		return EOPNOTSUPP; /* XXX why not ENODEV? */
243	}
244
245	vflushbuf(vp, 0);
246
247	mutex_enter(&chmp->chm_lock_mountfields);
248	chfs_flush_pending_wbuf(chmp);
249
250	/* handle truncate to zero as a special case */
251	if (size == 0) {
252		dbg("truncate to zero");
253		chfs_truncate_fragtree(ip->chmp,
254		    &ip->fragtree, size);
255		chfs_set_vnode_size(vp, size);
256
257		mutex_exit(&chmp->chm_lock_mountfields);
258
259		return 0;
260	}
261
262
263	/* allocate zeros for the new data */
264	buf = kmem_zalloc(size, KM_SLEEP);
265	bp = getiobuf(vp, true);
266
267	if (ip->size != 0) {
268		/* read the whole data */
269		bp->b_blkno = 0;
270		bp->b_bufsize = bp->b_resid = bp->b_bcount = ip->size;
271		bp->b_data = kmem_alloc(ip->size, KM_SLEEP);
272
273		error = chfs_read_data(chmp, vp, bp);
274		if (error) {
275			mutex_exit(&chmp->chm_lock_mountfields);
276			putiobuf(bp);
277
278			return error;
279		}
280
281		/* create the new data */
282		dbg("create new data vap%llu ip%llu\n",
283			(unsigned long long)size, (unsigned long long)ip->size);
284		append = size - ip->size;
285		if (append > 0) {
286			memcpy(buf, bp->b_data, ip->size);
287		} else {
288			memcpy(buf, bp->b_data, size);
289			chfs_truncate_fragtree(ip->chmp,
290				&ip->fragtree, size);
291		}
292
293		kmem_free(bp->b_data, ip->size);
294
295		struct chfs_node_frag *lastfrag = frag_last(&ip->fragtree);
296		fd = lastfrag->node;
297		chfs_mark_node_obsolete(chmp, fd->nref);
298
299		blknum = lastfrag->ofs / PAGE_SIZE;
300		lastfrag->size = append > PAGE_SIZE ? PAGE_SIZE : size % PAGE_SIZE;
301	} else {
302		fd = chfs_alloc_full_dnode();
303		blknum = 0;
304	}
305
306	chfs_set_vnode_size(vp, size);
307
308	// write the new data
309	for (bp->b_blkno = blknum; bp->b_blkno * PAGE_SIZE < size; bp->b_blkno++) {
310		uint64_t writesize = MIN(size - bp->b_blkno * PAGE_SIZE, PAGE_SIZE);
311
312		bp->b_bufsize = bp->b_resid = bp->b_bcount = writesize;
313		bp->b_data = kmem_alloc(writesize, KM_SLEEP);
314
315		memcpy(bp->b_data, buf + (bp->b_blkno * PAGE_SIZE), writesize);
316
317		if (bp->b_blkno != blknum) {
318			fd = chfs_alloc_full_dnode();
319		}
320
321		error = chfs_write_flash_dnode(chmp, vp, bp, fd);
322		if (error) {
323			mutex_exit(&chmp->chm_lock_mountfields);
324			kmem_free(bp->b_data, writesize);
325			putiobuf(bp);
326
327			return error;
328		}
329		if (bp->b_blkno != blknum) {
330			chfs_add_full_dnode_to_inode(chmp, ip, fd);
331		}
332		kmem_free(bp->b_data, writesize);
333	}
334
335	mutex_exit(&chmp->chm_lock_mountfields);
336
337	kmem_free(buf, size);
338	putiobuf(bp);
339
340	return 0;
341}
342#if 0
343	int error;
344	struct chfs_node *node;
345
346	KASSERT(VOP_ISLOCKED(vp));
347
348	node = VP_TO_CHFS_NODE(vp);
349
350	// Decide whether this is a valid operation based on the file type.
351	error = 0;
352	switch (vp->v_type) {
353	case VDIR:
354		return EISDIR;
355
356	case VREG:
357		if (vp->v_mount->mnt_flag & MNT_RDONLY)
358			return EROFS;
359		break;
360
361	case VBLK:
362	case VCHR:
363	case VFIFO:
364		// Allow modifications of special files even if in the file
365		// system is mounted read-only (we are not modifying the
366		// files themselves, but the objects they represent).
367		return 0;
368
369	default:
370		return ENODEV;
371	}
372
373	// Immutable or append-only files cannot be modified, either.
374	if (node->chn_flags & (IMMUTABLE | APPEND))
375		return EPERM;
376
377	error = chfs_truncate(vp, size);
378	// chfs_truncate will raise the NOTE_EXTEND and NOTE_ATTRIB kevents
379	// for us, as will update dn_status; no need to do that here.
380
381	KASSERT(VOP_ISLOCKED(vp));
382
383	return error;
384#endif
385
386/* --------------------------------------------------------------------- */
387
388/*
389 * Change flags of the given vnode.
390 * Caller should execute chfs_update on vp after a successful execution.
391 * The vnode must be locked on entry and remain locked on exit.
392 */
393int
394chfs_chflags(struct vnode *vp, int flags, kauth_cred_t cred)
395{
396	struct chfs_mount *chmp;
397	struct chfs_inode *ip;
398	int error = 0;
399
400	ip = VTOI(vp);
401	chmp = ip->chmp;
402
403	if (vp->v_mount->mnt_flag & MNT_RDONLY)
404		return EROFS;
405
406	if (kauth_cred_geteuid(cred) != ip->uid &&
407	    (error = kauth_authorize_generic(cred,
408		KAUTH_GENERIC_ISSUSER, NULL)))
409		return error;
410
411	if (kauth_authorize_generic(cred, KAUTH_GENERIC_ISSUSER,
412		NULL) == 0) {
413		if ((ip->flags & (SF_IMMUTABLE | SF_APPEND)) &&
414		    kauth_authorize_system(curlwp->l_cred,
415			KAUTH_SYSTEM_CHSYSFLAGS, 0, NULL, NULL, NULL))
416			return EPERM;
417
418		if ((flags & SF_SNAPSHOT) !=
419		    (ip->flags & SF_SNAPSHOT))
420			return EPERM;
421
422		ip->flags = flags;
423	} else {
424		if ((ip->flags & (SF_IMMUTABLE | SF_APPEND)) ||
425		    (flags & UF_SETTABLE) != flags)
426			return EPERM;
427
428		if ((ip->flags & SF_SETTABLE) !=
429		    (flags & SF_SETTABLE))
430			return EPERM;
431
432		ip->flags &= SF_SETTABLE;
433		ip->flags |= (flags & UF_SETTABLE);
434	}
435	ip->iflag |= IN_CHANGE;
436	error = chfs_update(vp, NULL, NULL, UPDATE_WAIT);
437	if (error)
438		return error;
439
440	if (flags & (IMMUTABLE | APPEND))
441		return 0;
442
443	return error;
444}
445
446/* --------------------------------------------------------------------- */
447
448void
449chfs_itimes(struct chfs_inode *ip, const struct timespec *acc,
450    const struct timespec *mod, const struct timespec *cre)
451{
452	//dbg("itimes\n");
453	struct timespec now;
454
455	if (!(ip->iflag & (IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY))) {
456		return;
457	}
458
459	vfs_timestamp(&now);
460	if (ip->iflag & IN_ACCESS) {
461		if (acc == NULL)
462			acc = &now;
463		ip->atime = acc->tv_sec;
464	}
465	if (ip->iflag & (IN_UPDATE | IN_MODIFY)) {
466		if (mod == NULL)
467			mod = &now;
468		ip->mtime = mod->tv_sec;
469		//ip->i_modrev++;
470	}
471	if (ip->iflag & (IN_CHANGE | IN_MODIFY)) {
472		if (cre == NULL)
473			cre = &now;
474		ip->ctime = cre->tv_sec;
475	}
476	if (ip->iflag & (IN_ACCESS | IN_MODIFY))
477		ip->iflag |= IN_ACCESSED;
478	if (ip->iflag & (IN_UPDATE | IN_CHANGE))
479		ip->iflag |= IN_MODIFIED;
480	ip->iflag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY);
481}
482
483/* --------------------------------------------------------------------- */
484
485int
486chfs_update(struct vnode *vp, const struct timespec *acc,
487    const struct timespec *mod, int flags)
488{
489
490	struct chfs_inode *ip;
491
492	/* XXX ufs_reclaim calls this function unlocked! */
493//	KASSERT(VOP_ISLOCKED(vp));
494
495#if 0
496	if (flags & UPDATE_CLOSE)
497		; /* XXX Need to do anything special? */
498#endif
499
500	ip = VTOI(vp);
501	chfs_itimes(ip, acc, mod, NULL);
502
503//	KASSERT(VOP_ISLOCKED(vp));
504	return (0);
505}
506
507/* --------------------------------------------------------------------- */
508/*
509  int
510  chfs_truncate(struct vnode *vp, off_t length)
511  {
512  bool extended;
513  int error;
514  struct chfs_node *node;
515  printf("CHFS: truncate()\n");
516
517  node = VP_TO_CHFS_NODE(vp);
518  extended = length > node->chn_size;
519
520  if (length < 0) {
521  error = EINVAL;
522  goto out;
523  }
524
525  if (node->chn_size == length) {
526  error = 0;
527  goto out;
528  }
529
530  error = chfs_reg_resize(vp, length);
531  if (error == 0)
532  node->chn_status |= CHFS_NODE_CHANGED | CHFS_NODE_MODIFIED;
533
534  out:
535  chfs_update(vp, NULL, NULL, 0);
536
537  return error;
538  }*/
539
540
541