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