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