xref: /netbsd-src/sys/ufs/chfs/chfs_subr.c (revision 9fc453562f6ebe8eabdfd51e21ae0a0058906d4f)
1*9fc45356Sriastradh /*	$NetBSD: chfs_subr.c,v 1.15 2020/09/05 16:30:12 riastradh Exp $	*/
2288addd0Sahoka 
3288addd0Sahoka /*-
4288addd0Sahoka  * Copyright (c) 2010 Department of Software Engineering,
5288addd0Sahoka  *		      University of Szeged, Hungary
6288addd0Sahoka  * Copyright (C) 2010 Tamas Toth <ttoth@inf.u-szeged.hu>
7288addd0Sahoka  * Copyright (C) 2010 Adam Hoka <ahoka@NetBSD.org>
8288addd0Sahoka  * All rights reserved.
9288addd0Sahoka  *
10288addd0Sahoka  * This code is derived from software contributed to The NetBSD Foundation
11288addd0Sahoka  * by the Department of Software Engineering, University of Szeged, Hungary
12288addd0Sahoka  *
13288addd0Sahoka  * Redistribution and use in source and binary forms, with or without
14288addd0Sahoka  * modification, are permitted provided that the following conditions
15288addd0Sahoka  * are met:
16288addd0Sahoka  * 1. Redistributions of source code must retain the above copyright
17288addd0Sahoka  *    notice, this list of conditions and the following disclaimer.
18288addd0Sahoka  * 2. Redistributions in binary form must reproduce the above copyright
19288addd0Sahoka  *    notice, this list of conditions and the following disclaimer in the
20288addd0Sahoka  *    documentation and/or other materials provided with the distribution.
21288addd0Sahoka  *
22288addd0Sahoka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23288addd0Sahoka  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24288addd0Sahoka  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25288addd0Sahoka  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26288addd0Sahoka  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27288addd0Sahoka  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28288addd0Sahoka  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29288addd0Sahoka  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30288addd0Sahoka  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31288addd0Sahoka  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32288addd0Sahoka  * SUCH DAMAGE.
33288addd0Sahoka  */
34288addd0Sahoka 
35288addd0Sahoka #include <sys/cdefs.h>
36288addd0Sahoka 
37288addd0Sahoka #include <sys/param.h>
38288addd0Sahoka #include <sys/dirent.h>
39288addd0Sahoka #include <sys/event.h>
40288addd0Sahoka #include <sys/kmem.h>
41288addd0Sahoka #include <sys/mount.h>
42288addd0Sahoka #include <sys/namei.h>
43288addd0Sahoka #include <sys/time.h>
44288addd0Sahoka #include <sys/stat.h>
45288addd0Sahoka #include <sys/systm.h>
46288addd0Sahoka #include <sys/swap.h>
47288addd0Sahoka #include <sys/vnode.h>
48288addd0Sahoka #include <sys/kauth.h>
49288addd0Sahoka #include <sys/proc.h>
50288addd0Sahoka #include <sys/atomic.h>
51288addd0Sahoka 
52*9fc45356Sriastradh #include <uvm/uvm_extern.h>
53288addd0Sahoka 
54288addd0Sahoka #include <miscfs/specfs/specdev.h>
550c9d8d15Selad #include <miscfs/genfs/genfs.h>
56288addd0Sahoka #include "chfs.h"
57288addd0Sahoka 
58288addd0Sahoka 
59288addd0Sahoka /*
60bca84b9eSttoth  * chfs_mem_info -
61288addd0Sahoka  * Returns information about the number of available memory pages,
62288addd0Sahoka  * including physical and virtual ones.
63288addd0Sahoka  *
64288addd0Sahoka  * If 'total' is true, the value returned is the total amount of memory
65288addd0Sahoka  * pages configured for the system (either in use or free).
66288addd0Sahoka  * If it is FALSE, the value returned is the amount of free memory pages.
67288addd0Sahoka  *
68288addd0Sahoka  * Remember to remove DUMMYFS_PAGES_RESERVED from the returned value to avoid
69288addd0Sahoka  * excessive memory usage.
70288addd0Sahoka  *
71288addd0Sahoka  */
72288addd0Sahoka size_t
chfs_mem_info(bool total)73288addd0Sahoka chfs_mem_info(bool total)
74288addd0Sahoka {
75288addd0Sahoka 	size_t size;
76288addd0Sahoka 
77288addd0Sahoka 	size = 0;
78288addd0Sahoka 	size += uvmexp.swpgavail;
79288addd0Sahoka 	if (!total) {
80288addd0Sahoka 		size -= uvmexp.swpgonly;
81288addd0Sahoka 	}
824b8a875aSad 	size += uvm_availmem(true);
83288addd0Sahoka 	size += uvmexp.filepages;
84288addd0Sahoka 	if (size > uvmexp.wired) {
85288addd0Sahoka 		size -= uvmexp.wired;
86288addd0Sahoka 	} else {
87288addd0Sahoka 		size = 0;
88288addd0Sahoka 	}
89288addd0Sahoka 
90288addd0Sahoka 	return size;
91288addd0Sahoka }
92288addd0Sahoka 
93288addd0Sahoka 
94288addd0Sahoka /*
95bca84b9eSttoth  * chfs_dir_lookup -
96288addd0Sahoka  * Looks for a directory entry in the directory represented by node.
97288addd0Sahoka  * 'cnp' describes the name of the entry to look for.  Note that the .
98288addd0Sahoka  * and .. components are not allowed as they do not physically exist
99288addd0Sahoka  * within directories.
100288addd0Sahoka  *
101288addd0Sahoka  * Returns a pointer to the entry when found, otherwise NULL.
102288addd0Sahoka  */
103288addd0Sahoka struct chfs_dirent *
chfs_dir_lookup(struct chfs_inode * ip,struct componentname * cnp)104288addd0Sahoka chfs_dir_lookup(struct chfs_inode *ip, struct componentname *cnp)
105288addd0Sahoka {
106288addd0Sahoka 	bool found;
107288addd0Sahoka 	struct chfs_dirent *fd;
108288addd0Sahoka 	dbg("dir_lookup()\n");
109288addd0Sahoka 
110288addd0Sahoka 	KASSERT(IMPLIES(cnp->cn_namelen == 1, cnp->cn_nameptr[0] != '.'));
111288addd0Sahoka 	KASSERT(IMPLIES(cnp->cn_namelen == 2, !(cnp->cn_nameptr[0] == '.' &&
112288addd0Sahoka 		    cnp->cn_nameptr[1] == '.')));
113288addd0Sahoka 
114288addd0Sahoka 	found = false;
115288addd0Sahoka 	TAILQ_FOREACH(fd, &ip->dents, fds) {
116288addd0Sahoka 		KASSERT(cnp->cn_namelen < 0xffff);
117288addd0Sahoka 		if (fd->vno == 0)
118288addd0Sahoka 			continue;
119288addd0Sahoka 		if (fd->nsize == (uint16_t)cnp->cn_namelen &&
120288addd0Sahoka 		    memcmp(fd->name, cnp->cn_nameptr, fd->nsize) == 0) {
121288addd0Sahoka 			found = true;
122288addd0Sahoka 			break;
123288addd0Sahoka 		}
124288addd0Sahoka 	}
125288addd0Sahoka 
126288addd0Sahoka 	return found ? fd : NULL;
127288addd0Sahoka }
128288addd0Sahoka 
129bca84b9eSttoth /*
130bca84b9eSttoth  * chfs_filldir -
131bca84b9eSttoth  * Creates a (kernel) dirent and moves it to the given memory address.
132bca84b9eSttoth  * Used during readdir.
133bca84b9eSttoth  */
134288addd0Sahoka int
chfs_filldir(struct uio * uio,ino_t ino,const char * name,int namelen,enum chtype type)135288addd0Sahoka chfs_filldir(struct uio* uio, ino_t ino, const char *name,
1364024b549Sttoth     int namelen, enum chtype type)
137288addd0Sahoka {
138288addd0Sahoka 	struct dirent dent;
139288addd0Sahoka 	int error;
140288addd0Sahoka 
141288addd0Sahoka 	memset(&dent, 0, sizeof(dent));
142288addd0Sahoka 
143288addd0Sahoka 	dent.d_fileno = ino;
144288addd0Sahoka 	switch (type) {
1454024b549Sttoth 	case CHT_BLK:
146288addd0Sahoka 		dent.d_type = DT_BLK;
147288addd0Sahoka 		break;
148288addd0Sahoka 
1494024b549Sttoth 	case CHT_CHR:
150288addd0Sahoka 		dent.d_type = DT_CHR;
151288addd0Sahoka 		break;
152288addd0Sahoka 
1534024b549Sttoth 	case CHT_DIR:
154288addd0Sahoka 		dent.d_type = DT_DIR;
155288addd0Sahoka 		break;
156288addd0Sahoka 
1574024b549Sttoth 	case CHT_FIFO:
158288addd0Sahoka 		dent.d_type = DT_FIFO;
159288addd0Sahoka 		break;
160288addd0Sahoka 
1614024b549Sttoth 	case CHT_LNK:
162288addd0Sahoka 		dent.d_type = DT_LNK;
163288addd0Sahoka 		break;
164288addd0Sahoka 
1654024b549Sttoth 	case CHT_REG:
166288addd0Sahoka 		dent.d_type = DT_REG;
167288addd0Sahoka 		break;
168288addd0Sahoka 
1694024b549Sttoth 	case CHT_SOCK:
170288addd0Sahoka 		dent.d_type = DT_SOCK;
171288addd0Sahoka 		break;
172288addd0Sahoka 
173288addd0Sahoka 	default:
174288addd0Sahoka 		KASSERT(0);
175288addd0Sahoka 	}
176288addd0Sahoka 	dent.d_namlen = namelen;
177288addd0Sahoka 	(void)memcpy(dent.d_name, name, dent.d_namlen);
178288addd0Sahoka 	dent.d_reclen = _DIRENT_SIZE(&dent);
179288addd0Sahoka 
180288addd0Sahoka 	if (dent.d_reclen > uio->uio_resid) {
181288addd0Sahoka 		error = -1;
182288addd0Sahoka 	} else {
183288addd0Sahoka 		error = uiomove(&dent, dent.d_reclen, uio);
184288addd0Sahoka 	}
185288addd0Sahoka 
186288addd0Sahoka 	return error;
187288addd0Sahoka }
188288addd0Sahoka 
189288addd0Sahoka /*
190bca84b9eSttoth  * chfs_chsize - change size of the given vnode
191288addd0Sahoka  * Caller should execute chfs_update on vp after a successful execution.
192288addd0Sahoka  * The vnode must be locked on entry and remain locked on exit.
193288addd0Sahoka  */
194288addd0Sahoka int
chfs_chsize(struct vnode * vp,u_quad_t size,kauth_cred_t cred)195288addd0Sahoka chfs_chsize(struct vnode *vp, u_quad_t size, kauth_cred_t cred)
196288addd0Sahoka {
197288addd0Sahoka 	struct chfs_mount *chmp;
198288addd0Sahoka 	struct chfs_inode *ip;
199288addd0Sahoka 
200288addd0Sahoka 	ip = VTOI(vp);
201288addd0Sahoka 	chmp = ip->chmp;
202288addd0Sahoka 
203288addd0Sahoka 	dbg("chfs_chsize\n");
204288addd0Sahoka 
2054024b549Sttoth 	switch (ip->ch_type) {
2064024b549Sttoth 	case CHT_DIR:
207288addd0Sahoka 		return EISDIR;
2084024b549Sttoth 	case CHT_LNK:
2094024b549Sttoth 	case CHT_REG:
210288addd0Sahoka 		if (vp->v_mount->mnt_flag & MNT_RDONLY)
211288addd0Sahoka 			return EROFS;
212288addd0Sahoka 		break;
2134024b549Sttoth 	case CHT_BLK:
2144024b549Sttoth 	case CHT_CHR:
2154024b549Sttoth 	case CHT_FIFO:
216288addd0Sahoka 		return 0;
217288addd0Sahoka 	default:
218288addd0Sahoka 		return EOPNOTSUPP; /* XXX why not ENODEV? */
219288addd0Sahoka 	}
220288addd0Sahoka 
221288addd0Sahoka 	vflushbuf(vp, 0);
222288addd0Sahoka 
223288addd0Sahoka 	mutex_enter(&chmp->chm_lock_mountfields);
224288addd0Sahoka 
2258a77fce6Sttoth 	if (ip->size < size) {
2268a77fce6Sttoth 		uvm_vnp_setsize(vp, size);
227288addd0Sahoka 		chfs_set_vnode_size(vp, size);
2288a77fce6Sttoth 		ip->iflag |= IN_CHANGE | IN_UPDATE;
229288addd0Sahoka 
230288addd0Sahoka 		mutex_exit(&chmp->chm_lock_mountfields);
231288addd0Sahoka 		return 0;
232288addd0Sahoka 	}
233288addd0Sahoka 
2348a77fce6Sttoth 	if (size != 0) {
235f5ad84fdSad 		ubc_zerorange(&vp->v_uobj, size, ip->size - size, UBC_VNODE_FLAGS(vp));
236288addd0Sahoka 	}
237288addd0Sahoka 
238bca84b9eSttoth 	/* drop unused fragments */
2398a77fce6Sttoth 	chfs_truncate_fragtree(ip->chmp, &ip->fragtree, size);
240bca84b9eSttoth 
2418a77fce6Sttoth 	uvm_vnp_setsize(vp, size);
242288addd0Sahoka 	chfs_set_vnode_size(vp, size);
2438a77fce6Sttoth 	ip->iflag |= IN_CHANGE | IN_UPDATE;
244288addd0Sahoka 	mutex_exit(&chmp->chm_lock_mountfields);
245288addd0Sahoka 	return 0;
246288addd0Sahoka }
247288addd0Sahoka 
248288addd0Sahoka /*
249bca84b9eSttoth  * chfs_chflags - change flags of the given vnode
250288addd0Sahoka  * Caller should execute chfs_update on vp after a successful execution.
251288addd0Sahoka  * The vnode must be locked on entry and remain locked on exit.
252288addd0Sahoka  */
253288addd0Sahoka int
chfs_chflags(struct vnode * vp,int flags,kauth_cred_t cred)254288addd0Sahoka chfs_chflags(struct vnode *vp, int flags, kauth_cred_t cred)
255288addd0Sahoka {
256288addd0Sahoka 	struct chfs_inode *ip;
257288addd0Sahoka 	int error = 0;
2580c9d8d15Selad 	kauth_action_t action = KAUTH_VNODE_WRITE_FLAGS;
2590c9d8d15Selad 	bool changing_sysflags = false;
260288addd0Sahoka 
261288addd0Sahoka 	ip = VTOI(vp);
262288addd0Sahoka 
263288addd0Sahoka 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
264288addd0Sahoka 		return EROFS;
265288addd0Sahoka 
2660c9d8d15Selad 	if ((flags & SF_SNAPSHOT) != (ip->flags & SF_SNAPSHOT))
2670c9d8d15Selad 		return EPERM;
2680c9d8d15Selad 
2690c9d8d15Selad 	/* Indicate we're changing system flags if we are. */
2700c9d8d15Selad 	if ((ip->flags & SF_SETTABLE) != (flags & SF_SETTABLE) ||
2710c9d8d15Selad 	    (flags & UF_SETTABLE) != flags) {
2720c9d8d15Selad 		action |= KAUTH_VNODE_WRITE_SYSFLAGS;
2730c9d8d15Selad 		changing_sysflags = true;
2740c9d8d15Selad 	}
2750c9d8d15Selad 
2760c9d8d15Selad 	/* Indicate the node has system flags if it does. */
2770c9d8d15Selad 	if (ip->flags & (SF_IMMUTABLE | SF_APPEND)) {
2780c9d8d15Selad 		action |= KAUTH_VNODE_HAS_SYSFLAGS;
2790c9d8d15Selad 	}
2800c9d8d15Selad 
2810c9d8d15Selad 	error = kauth_authorize_vnode(cred, action, vp, NULL,
2829aa2a9c3Schristos 	    genfs_can_chflags(vp, cred, ip->uid, changing_sysflags));
2830c9d8d15Selad 	if (error)
284288addd0Sahoka 		return error;
285288addd0Sahoka 
2860c9d8d15Selad 	if (changing_sysflags) {
287288addd0Sahoka 		ip->flags = flags;
288288addd0Sahoka 	} else {
289288addd0Sahoka 		ip->flags &= SF_SETTABLE;
290288addd0Sahoka 		ip->flags |= (flags & UF_SETTABLE);
291288addd0Sahoka 	}
292288addd0Sahoka 	ip->iflag |= IN_CHANGE;
293288addd0Sahoka 	error = chfs_update(vp, NULL, NULL, UPDATE_WAIT);
294288addd0Sahoka 	if (error)
295288addd0Sahoka 		return error;
296288addd0Sahoka 
297288addd0Sahoka 	if (flags & (IMMUTABLE | APPEND))
298288addd0Sahoka 		return 0;
299288addd0Sahoka 
300288addd0Sahoka 	return error;
301288addd0Sahoka }
302288addd0Sahoka 
303288addd0Sahoka 
304bca84b9eSttoth /* chfs_itimes - updates a vnode times to the given data */
305288addd0Sahoka void
chfs_itimes(struct chfs_inode * ip,const struct timespec * acc,const struct timespec * mod,const struct timespec * cre)306288addd0Sahoka chfs_itimes(struct chfs_inode *ip, const struct timespec *acc,
307288addd0Sahoka     const struct timespec *mod, const struct timespec *cre)
308288addd0Sahoka {
309288addd0Sahoka 	struct timespec now;
310288addd0Sahoka 
311288addd0Sahoka 	if (!(ip->iflag & (IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY))) {
312288addd0Sahoka 		return;
313288addd0Sahoka 	}
314288addd0Sahoka 
315288addd0Sahoka 	vfs_timestamp(&now);
316288addd0Sahoka 	if (ip->iflag & IN_ACCESS) {
317288addd0Sahoka 		if (acc == NULL)
318288addd0Sahoka 			acc = &now;
319288addd0Sahoka 		ip->atime = acc->tv_sec;
320288addd0Sahoka 	}
321288addd0Sahoka 	if (ip->iflag & (IN_UPDATE | IN_MODIFY)) {
322288addd0Sahoka 		if (mod == NULL)
323288addd0Sahoka 			mod = &now;
324288addd0Sahoka 		ip->mtime = mod->tv_sec;
325288addd0Sahoka 	}
326288addd0Sahoka 	if (ip->iflag & (IN_CHANGE | IN_MODIFY)) {
327288addd0Sahoka 		if (cre == NULL)
328288addd0Sahoka 			cre = &now;
329288addd0Sahoka 		ip->ctime = cre->tv_sec;
330288addd0Sahoka 	}
331288addd0Sahoka 	if (ip->iflag & (IN_ACCESS | IN_MODIFY))
332288addd0Sahoka 		ip->iflag |= IN_ACCESSED;
333288addd0Sahoka 	if (ip->iflag & (IN_UPDATE | IN_CHANGE))
334288addd0Sahoka 		ip->iflag |= IN_MODIFIED;
335288addd0Sahoka 	ip->iflag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE | IN_MODIFY);
336288addd0Sahoka }
337288addd0Sahoka 
338bca84b9eSttoth /* chfs_update - updates a vnode times */
339288addd0Sahoka int
chfs_update(struct vnode * vp,const struct timespec * acc,const struct timespec * mod,int flags)340288addd0Sahoka chfs_update(struct vnode *vp, const struct timespec *acc,
341288addd0Sahoka     const struct timespec *mod, int flags)
342288addd0Sahoka {
343288addd0Sahoka 	struct chfs_inode *ip;
344288addd0Sahoka 
345288addd0Sahoka 	/* XXX ufs_reclaim calls this function unlocked! */
346288addd0Sahoka 
347288addd0Sahoka 	ip = VTOI(vp);
348288addd0Sahoka 	chfs_itimes(ip, acc, mod, NULL);
349288addd0Sahoka 
350288addd0Sahoka 	return (0);
351288addd0Sahoka }
352288addd0Sahoka 
353