chfs_subr.c revision 1.2 1 /* $NetBSD: chfs_subr.c,v 1.2 2011/11/24 21:09:37 agc Exp $ */
2
3 /*-
4 * Copyright (c) 2010 Department of Software Engineering,
5 * University of Szeged, Hungary
6 * Copyright (C) 2010 Tamas Toth <ttoth (at) inf.u-szeged.hu>
7 * Copyright (C) 2010 Adam Hoka <ahoka (at) 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 */
77 size_t
78 chfs_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 */
109 struct chfs_dirent *
110 chfs_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
150 int
151 chfs_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 */
213 int
214 chfs_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 */
393 int
394 chfs_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
448 void
449 chfs_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
485 int
486 chfs_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