/*1* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 20092* The President and Fellows of Harvard College.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. Neither the name of the University nor the names of its contributors13* may be used to endorse or promote products derived from this software14* without specific prior written permission.15*16* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829/*30* SFS filesystem31*32* File-level (vnode) interface routines.33*/34#include <types.h>35#include <kern/errno.h>36#include <kern/fcntl.h>37#include <stat.h>38#include <lib.h>39#include <array.h>40#include <bitmap.h>41#include <uio.h>42#include <synch.h>43#include <vfs.h>44#include <device.h>45#include <sfs.h>4647/* At bottom of file */48static int sfs_loadvnode(struct sfs_fs *sfs, uint32_t ino, int type,49struct sfs_vnode **ret);5051////////////////////////////////////////////////////////////52//53// Simple stuff5455/* Zero out a disk block. */56static57int58sfs_clearblock(struct sfs_fs *sfs, uint32_t block)59{60/* static -> automatically initialized to zero */61static char zeros[SFS_BLOCKSIZE];62return sfs_wblock(sfs, zeros, block);63}6465/* Write an on-disk inode structure back out to disk. */66static67int68sfs_sync_inode(struct sfs_vnode *sv)69{70if (sv->sv_dirty) {71struct sfs_fs *sfs = sv->sv_v.vn_fs->fs_data;72int result = sfs_wblock(sfs, &sv->sv_i, sv->sv_ino);73if (result) {74return result;75}76sv->sv_dirty = false;77}78return 0;79}8081////////////////////////////////////////////////////////////82//83// Space allocation8485/*86* Allocate a block.87*/88static89int90sfs_balloc(struct sfs_fs *sfs, uint32_t *diskblock)91{92int result;9394result = bitmap_alloc(sfs->sfs_freemap, diskblock);95if (result) {96return result;97}98sfs->sfs_freemapdirty = true;99100if (*diskblock >= sfs->sfs_super.sp_nblocks) {101panic("sfs: balloc: invalid block %u\n", *diskblock);102}103104/* Clear block before returning it */105return sfs_clearblock(sfs, *diskblock);106}107108/*109* Free a block.110*/111static112void113sfs_bfree(struct sfs_fs *sfs, uint32_t diskblock)114{115bitmap_unmark(sfs->sfs_freemap, diskblock);116sfs->sfs_freemapdirty = true;117}118119/*120* Check if a block is in use.121*/122static123int124sfs_bused(struct sfs_fs *sfs, uint32_t diskblock)125{126if (diskblock >= sfs->sfs_super.sp_nblocks) {127panic("sfs: sfs_bused called on out of range block %u\n",128diskblock);129}130return bitmap_isset(sfs->sfs_freemap, diskblock);131}132133////////////////////////////////////////////////////////////134//135// Block mapping/inode maintenance136137/*138* Look up the disk block number (from 0 up to the number of blocks on139* the disk) given a file and the logical block number within that140* file. If DOALLOC is set, and no such block exists, one will be141* allocated.142*/143static144int145sfs_bmap(struct sfs_vnode *sv, uint32_t fileblock, int doalloc,146uint32_t *diskblock)147{148/*149* I/O buffer for handling indirect blocks.150*151* Note: in real life (and when you've done the fs assignment)152* you would get space from the disk buffer cache for this,153* not use a static area.154*/155static uint32_t idbuf[SFS_DBPERIDB];156157struct sfs_fs *sfs = sv->sv_v.vn_fs->fs_data;158uint32_t block;159uint32_t idblock;160uint32_t idnum, idoff;161int result;162163KASSERT(sizeof(idbuf)==SFS_BLOCKSIZE);164165/*166* If the block we want is one of the direct blocks...167*/168if (fileblock < SFS_NDIRECT) {169/*170* Get the block number171*/172block = sv->sv_i.sfi_direct[fileblock];173174/*175* Do we need to allocate?176*/177if (block==0 && doalloc) {178result = sfs_balloc(sfs, &block);179if (result) {180return result;181}182183/* Remember what we allocated; mark inode dirty */184sv->sv_i.sfi_direct[fileblock] = block;185sv->sv_dirty = true;186}187188/*189* Hand back the block190*/191if (block != 0 && !sfs_bused(sfs, block)) {192panic("sfs: Data block %u (block %u of file %u) "193"marked free\n", block, fileblock, sv->sv_ino);194}195*diskblock = block;196return 0;197}198199/*200* It's not a direct block; it must be in the indirect block.201* Subtract off the number of direct blocks, so FILEBLOCK is202* now the offset into the indirect block space.203*/204205fileblock -= SFS_NDIRECT;206207/* Get the indirect block number and offset w/i that indirect block */208idnum = fileblock / SFS_DBPERIDB;209idoff = fileblock % SFS_DBPERIDB;210211/*212* We only have one indirect block. If the offset we were asked for213* is too large, we can't handle it, so fail.214*/215if (idnum > 0) {216return EFBIG;217}218219/* Get the disk block number of the indirect block. */220idblock = sv->sv_i.sfi_indirect;221222if (idblock==0 && !doalloc) {223/*224* There's no indirect block allocated. We weren't225* asked to allocate anything, so pretend the indirect226* block was filled with all zeros.227*/228*diskblock = 0;229return 0;230}231else if (idblock==0) {232/*233* There's no indirect block allocated, but we need to234* allocate a block whose number needs to be stored in235* the indirect block. Thus, we need to allocate an236* indirect block.237*/238result = sfs_balloc(sfs, &idblock);239if (result) {240return result;241}242243/* Remember the block we just allocated */244sv->sv_i.sfi_indirect = idblock;245246/* Mark the inode dirty */247sv->sv_dirty = true;248249/* Clear the indirect block buffer */250bzero(idbuf, sizeof(idbuf));251}252else {253/*254* We already have an indirect block allocated; load it.255*/256result = sfs_rblock(sfs, idbuf, idblock);257if (result) {258return result;259}260}261262/* Get the block out of the indirect block buffer */263block = idbuf[idoff];264265/* If there's no block there, allocate one */266if (block==0 && doalloc) {267result = sfs_balloc(sfs, &block);268if (result) {269return result;270}271272/* Remember the block we allocated */273idbuf[idoff] = block;274275/* The indirect block is now dirty; write it back */276result = sfs_wblock(sfs, idbuf, idblock);277if (result) {278return result;279}280}281282/* Hand back the result and return. */283if (block != 0 && !sfs_bused(sfs, block)) {284panic("sfs: Data block %u (block %u of file %u) marked free\n",285block, fileblock, sv->sv_ino);286}287*diskblock = block;288return 0;289}290291////////////////////////////////////////////////////////////292//293// File-level I/O294295/*296* Do I/O to a block of a file that doesn't cover the whole block. We297* need to read in the original block first, even if we're writing, so298* we don't clobber the portion of the block we're not intending to299* write over.300*301* skipstart is the number of bytes to skip past at the beginning of302* the sector; len is the number of bytes to actually read or write.303* uio is the area to do the I/O into.304*/305static306int307sfs_partialio(struct sfs_vnode *sv, struct uio *uio,308uint32_t skipstart, uint32_t len)309{310/*311* I/O buffer for handling partial sectors.312*313* Note: in real life (and when you've done the fs assignment)314* you would get space from the disk buffer cache for this,315* not use a static area.316*/317static char iobuf[SFS_BLOCKSIZE];318319struct sfs_fs *sfs = sv->sv_v.vn_fs->fs_data;320uint32_t diskblock;321uint32_t fileblock;322int result;323324/* Allocate missing blocks if and only if we're writing */325int doalloc = (uio->uio_rw==UIO_WRITE);326327KASSERT(skipstart + len <= SFS_BLOCKSIZE);328329/* Compute the block offset of this block in the file */330fileblock = uio->uio_offset / SFS_BLOCKSIZE;331332/* Get the disk block number */333result = sfs_bmap(sv, fileblock, doalloc, &diskblock);334if (result) {335return result;336}337338if (diskblock == 0) {339/*340* There was no block mapped at this point in the file.341* Zero the buffer.342*/343KASSERT(uio->uio_rw == UIO_READ);344bzero(iobuf, sizeof(iobuf));345}346else {347/*348* Read the block.349*/350result = sfs_rblock(sfs, iobuf, diskblock);351if (result) {352return result;353}354}355356/*357* Now perform the requested operation into/out of the buffer.358*/359result = uiomove(iobuf+skipstart, len, uio);360if (result) {361return result;362}363364/*365* If it was a write, write back the modified block.366*/367if (uio->uio_rw == UIO_WRITE) {368result = sfs_wblock(sfs, iobuf, diskblock);369if (result) {370return result;371}372}373374return 0;375}376377/*378* Do I/O (either read or write) of a single whole block.379*/380static381int382sfs_blockio(struct sfs_vnode *sv, struct uio *uio)383{384struct sfs_fs *sfs = sv->sv_v.vn_fs->fs_data;385uint32_t diskblock;386uint32_t fileblock;387int result;388int doalloc = (uio->uio_rw==UIO_WRITE);389off_t saveoff;390off_t diskoff;391off_t saveres;392off_t diskres;393394/* Get the block number within the file */395fileblock = uio->uio_offset / SFS_BLOCKSIZE;396397/* Look up the disk block number */398result = sfs_bmap(sv, fileblock, doalloc, &diskblock);399if (result) {400return result;401}402403if (diskblock == 0) {404/*405* No block - fill with zeros.406*407* We must be reading, or sfs_bmap would have408* allocated a block for us.409*/410KASSERT(uio->uio_rw == UIO_READ);411return uiomovezeros(SFS_BLOCKSIZE, uio);412}413414/*415* Do the I/O directly to the uio region. Save the uio_offset,416* and substitute one that makes sense to the device.417*/418saveoff = uio->uio_offset;419diskoff = diskblock * SFS_BLOCKSIZE;420uio->uio_offset = diskoff;421422/*423* Temporarily set the residue to be one block size.424*/425KASSERT(uio->uio_resid >= SFS_BLOCKSIZE);426saveres = uio->uio_resid;427diskres = SFS_BLOCKSIZE;428uio->uio_resid = diskres;429430result = sfs_rwblock(sfs, uio);431432/*433* Now, restore the original uio_offset and uio_resid and update434* them by the amount of I/O done.435*/436uio->uio_offset = (uio->uio_offset - diskoff) + saveoff;437uio->uio_resid = (uio->uio_resid - diskres) + saveres;438439return result;440}441442/*443* Do I/O of a whole region of data, whether or not it's block-aligned.444*/445static446int447sfs_io(struct sfs_vnode *sv, struct uio *uio)448{449uint32_t blkoff;450uint32_t nblocks, i;451int result = 0;452uint32_t extraresid = 0;453454/*455* If reading, check for EOF. If we can read a partial area,456* remember how much extra there was in EXTRARESID so we can457* add it back to uio_resid at the end.458*/459if (uio->uio_rw == UIO_READ) {460off_t size = sv->sv_i.sfi_size;461off_t endpos = uio->uio_offset + uio->uio_resid;462463if (uio->uio_offset >= size) {464/* At or past EOF - just return */465return 0;466}467468if (endpos > size) {469extraresid = endpos - size;470KASSERT(uio->uio_resid > extraresid);471uio->uio_resid -= extraresid;472}473}474475/*476* First, do any leading partial block.477*/478blkoff = uio->uio_offset % SFS_BLOCKSIZE;479if (blkoff != 0) {480/* Number of bytes at beginning of block to skip */481uint32_t skip = blkoff;482483/* Number of bytes to read/write after that point */484uint32_t len = SFS_BLOCKSIZE - blkoff;485486/* ...which might be less than the rest of the block */487if (len > uio->uio_resid) {488len = uio->uio_resid;489}490491/* Call sfs_partialio() to do it. */492result = sfs_partialio(sv, uio, skip, len);493if (result) {494goto out;495}496}497498/* If we're done, quit. */499if (uio->uio_resid==0) {500goto out;501}502503/*504* Now we should be block-aligned. Do the remaining whole blocks.505*/506KASSERT(uio->uio_offset % SFS_BLOCKSIZE == 0);507nblocks = uio->uio_resid / SFS_BLOCKSIZE;508for (i=0; i<nblocks; i++) {509result = sfs_blockio(sv, uio);510if (result) {511goto out;512}513}514515/*516* Now do any remaining partial block at the end.517*/518KASSERT(uio->uio_resid < SFS_BLOCKSIZE);519520if (uio->uio_resid > 0) {521result = sfs_partialio(sv, uio, 0, uio->uio_resid);522if (result) {523goto out;524}525}526527out:528529/* If writing, adjust file length */530if (uio->uio_rw == UIO_WRITE &&531uio->uio_offset > (off_t)sv->sv_i.sfi_size) {532sv->sv_i.sfi_size = uio->uio_offset;533sv->sv_dirty = true;534}535536/* Add in any extra amount we couldn't read because of EOF */537uio->uio_resid += extraresid;538539/* Done */540return result;541}542543////////////////////////////////////////////////////////////544//545// Directory I/O546547/*548* Read the directory entry out of slot SLOT of a directory vnode.549* The "slot" is the index of the directory entry, starting at 0.550*/551static552int553sfs_readdir(struct sfs_vnode *sv, struct sfs_dir *sd, int slot)554{555struct iovec iov;556struct uio ku;557off_t actualpos;558int result;559560/* Compute the actual position in the directory to read. */561actualpos = slot * sizeof(struct sfs_dir);562563/* Set up a uio to do the read */564uio_kinit(&iov, &ku, sd, sizeof(struct sfs_dir), actualpos, UIO_READ);565566/* do it */567result = sfs_io(sv, &ku);568if (result) {569return result;570}571572/* We should not hit EOF in the middle of a directory entry */573if (ku.uio_resid > 0) {574panic("sfs: readdir: Short entry (inode %u)\n", sv->sv_ino);575}576577/* Done */578return 0;579}580581/*582* Write (overwrite) the directory entry in slot SLOT of a directory583* vnode.584*/585static586int587sfs_writedir(struct sfs_vnode *sv, struct sfs_dir *sd, int slot)588{589struct iovec iov;590struct uio ku;591off_t actualpos;592int result;593594/* Compute the actual position in the directory. */595KASSERT(slot>=0);596actualpos = slot * sizeof(struct sfs_dir);597598/* Set up a uio to do the write */599uio_kinit(&iov, &ku, sd, sizeof(struct sfs_dir), actualpos, UIO_WRITE);600601/* do it */602result = sfs_io(sv, &ku);603if (result) {604return result;605}606607/* Should not end up with a partial entry! */608if (ku.uio_resid > 0) {609panic("sfs: writedir: Short write (ino %u)\n", sv->sv_ino);610}611612/* Done */613return 0;614}615616/*617* Compute the number of entries in a directory.618* This actually computes the number of existing slots, and does not619* account for empty slots.620*/621static622int623sfs_dir_nentries(struct sfs_vnode *sv)624{625off_t size;626627KASSERT(sv->sv_i.sfi_type == SFS_TYPE_DIR);628629size = sv->sv_i.sfi_size;630if (size % sizeof(struct sfs_dir) != 0) {631panic("sfs: directory %u: Invalid size %llu\n",632sv->sv_ino, size);633}634635return size / sizeof(struct sfs_dir);636}637638/*639* Search a directory for a particular filename in a directory, and640* return its inode number, its slot, and/or the slot number of an641* empty directory slot if one is found.642*/643644static645int646sfs_dir_findname(struct sfs_vnode *sv, const char *name,647uint32_t *ino, int *slot, int *emptyslot)648{649struct sfs_dir tsd;650int found = 0;651int nentries = sfs_dir_nentries(sv);652int i, result;653654/* For each slot... */655for (i=0; i<nentries; i++) {656657/* Read the entry from that slot */658result = sfs_readdir(sv, &tsd, i);659if (result) {660return result;661}662if (tsd.sfd_ino == SFS_NOINO) {663/* Free slot - report it back if one was requested */664if (emptyslot != NULL) {665*emptyslot = i;666}667}668else {669/* Ensure null termination, just in case */670tsd.sfd_name[sizeof(tsd.sfd_name)-1] = 0;671if (!strcmp(tsd.sfd_name, name)) {672673/* Each name may legally appear only once... */674KASSERT(found==0);675676found = 1;677if (slot != NULL) {678*slot = i;679}680if (ino != NULL) {681*ino = tsd.sfd_ino;682}683}684}685}686687return found ? 0 : ENOENT;688}689690/*691* Create a link in a directory to the specified inode by number, with692* the specified name, and optionally hand back the slot.693*/694static695int696sfs_dir_link(struct sfs_vnode *sv, const char *name, uint32_t ino, int *slot)697{698int emptyslot = -1;699int result;700struct sfs_dir sd;701702/* Look up the name. We want to make sure it *doesn't* exist. */703result = sfs_dir_findname(sv, name, NULL, NULL, &emptyslot);704if (result!=0 && result!=ENOENT) {705return result;706}707if (result==0) {708return EEXIST;709}710711if (strlen(name)+1 > sizeof(sd.sfd_name)) {712return ENAMETOOLONG;713}714715/* If we didn't get an empty slot, add the entry at the end. */716if (emptyslot < 0) {717emptyslot = sfs_dir_nentries(sv);718}719720/* Set up the entry. */721bzero(&sd, sizeof(sd));722sd.sfd_ino = ino;723strcpy(sd.sfd_name, name);724725/* Hand back the slot, if so requested. */726if (slot) {727*slot = emptyslot;728}729730/* Write the entry. */731return sfs_writedir(sv, &sd, emptyslot);732733}734735/*736* Unlink a name in a directory, by slot number.737*/738static739int740sfs_dir_unlink(struct sfs_vnode *sv, int slot)741{742struct sfs_dir sd;743744/* Initialize a suitable directory entry... */745bzero(&sd, sizeof(sd));746sd.sfd_ino = SFS_NOINO;747748/* ... and write it */749return sfs_writedir(sv, &sd, slot);750}751752/*753* Look for a name in a directory and hand back a vnode for the754* file, if there is one.755*/756static757int758sfs_lookonce(struct sfs_vnode *sv, const char *name,759struct sfs_vnode **ret,760int *slot)761{762struct sfs_fs *sfs = sv->sv_v.vn_fs->fs_data;763uint32_t ino;764int result;765766result = sfs_dir_findname(sv, name, &ino, slot, NULL);767if (result) {768return result;769}770771result = sfs_loadvnode(sfs, ino, SFS_TYPE_INVAL, ret);772if (result) {773return result;774}775776if ((*ret)->sv_i.sfi_linkcount == 0) {777panic("sfs: Link count of file %u found in dir %u is 0\n",778(*ret)->sv_ino, sv->sv_ino);779}780781return 0;782}783784////////////////////////////////////////////////////////////785//786// Object creation787788/*789* Create a new filesystem object and hand back its vnode.790*/791static792int793sfs_makeobj(struct sfs_fs *sfs, int type, struct sfs_vnode **ret)794{795uint32_t ino;796int result;797798/*799* First, get an inode. (Each inode is a block, and the inode800* number is the block number, so just get a block.)801*/802803result = sfs_balloc(sfs, &ino);804if (result) {805return result;806}807808/*809* Now load a vnode for it.810*/811812return sfs_loadvnode(sfs, ino, type, ret);813}814815////////////////////////////////////////////////////////////816//817// Vnode ops818819/*820* This is called on *each* open().821*/822static823int824sfs_open(struct vnode *v, int openflags)825{826/*827* At this level we do not need to handle O_CREAT, O_EXCL, or O_TRUNC.828* We *would* need to handle O_APPEND, but we don't support it.829*830* Any of O_RDONLY, O_WRONLY, and O_RDWR are valid, so we don't need831* to check that either.832*/833834if (openflags & O_APPEND) {835return EUNIMP;836}837838(void)v;839840return 0;841}842843/*844* This is called on *each* open() of a directory.845* Directories may only be open for read.846*/847static848int849sfs_opendir(struct vnode *v, int openflags)850{851switch (openflags & O_ACCMODE) {852case O_RDONLY:853break;854case O_WRONLY:855case O_RDWR:856default:857return EISDIR;858}859if (openflags & O_APPEND) {860return EISDIR;861}862863(void)v;864return 0;865}866867/*868* Called on the *last* close().869*870* This function should attempt to avoid returning errors, as handling871* them usefully is often not possible.872*/873static874int875sfs_close(struct vnode *v)876{877/* Sync it. */878return VOP_FSYNC(v);879}880881/*882* Called when the vnode refcount (in-memory usage count) hits zero.883*884* This function should try to avoid returning errors other than EBUSY.885*/886static887int888sfs_reclaim(struct vnode *v)889{890struct sfs_vnode *sv = v->vn_data;891struct sfs_fs *sfs = v->vn_fs->fs_data;892unsigned ix, i, num;893int result;894895vfs_biglock_acquire();896897/*898* Make sure someone else hasn't picked up the vnode since the899* decision was made to reclaim it. (You must also synchronize900* this with sfs_loadvnode.)901*/902if (v->vn_refcount != 1) {903904/* consume the reference VOP_DECREF gave us */905KASSERT(v->vn_refcount>1);906v->vn_refcount--;907908vfs_biglock_release();909return EBUSY;910}911912/* If there are no on-disk references to the file either, erase it. */913if (sv->sv_i.sfi_linkcount==0) {914result = VOP_TRUNCATE(&sv->sv_v, 0);915if (result) {916vfs_biglock_release();917return result;918}919}920921/* Sync the inode to disk */922result = sfs_sync_inode(sv);923if (result) {924vfs_biglock_release();925return result;926}927928/* If there are no on-disk references, discard the inode */929if (sv->sv_i.sfi_linkcount==0) {930sfs_bfree(sfs, sv->sv_ino);931}932933/* Remove the vnode structure from the table in the struct sfs_fs. */934num = vnodearray_num(sfs->sfs_vnodes);935ix = num;936for (i=0; i<num; i++) {937struct vnode *v2 = vnodearray_get(sfs->sfs_vnodes, i);938struct sfs_vnode *sv2 = v2->vn_data;939if (sv2 == sv) {940ix = i;941break;942}943}944if (ix == num) {945panic("sfs: reclaim vnode %u not in vnode pool\n",946sv->sv_ino);947}948vnodearray_remove(sfs->sfs_vnodes, ix);949950VOP_CLEANUP(&sv->sv_v);951952vfs_biglock_release();953954/* Release the storage for the vnode structure itself. */955kfree(sv);956957/* Done */958return 0;959}960961/*962* Called for read(). sfs_io() does the work.963*/964static965int966sfs_read(struct vnode *v, struct uio *uio)967{968struct sfs_vnode *sv = v->vn_data;969int result;970971KASSERT(uio->uio_rw==UIO_READ);972973vfs_biglock_acquire();974result = sfs_io(sv, uio);975vfs_biglock_release();976977return result;978}979980/*981* Called for write(). sfs_io() does the work.982*/983static984int985sfs_write(struct vnode *v, struct uio *uio)986{987struct sfs_vnode *sv = v->vn_data;988int result;989990KASSERT(uio->uio_rw==UIO_WRITE);991992vfs_biglock_acquire();993result = sfs_io(sv, uio);994vfs_biglock_release();995996return result;997}998999/*1000* Called for ioctl()1001*/1002static1003int1004sfs_ioctl(struct vnode *v, int op, userptr_t data)1005{1006/*1007* No ioctls.1008*/10091010(void)v;1011(void)op;1012(void)data;10131014return EINVAL;1015}10161017/*1018* Called for stat/fstat/lstat.1019*/1020static1021int1022sfs_stat(struct vnode *v, struct stat *statbuf)1023{1024struct sfs_vnode *sv = v->vn_data;1025int result;10261027/* Fill in the stat structure */1028bzero(statbuf, sizeof(struct stat));10291030result = VOP_GETTYPE(v, &statbuf->st_mode);1031if (result) {1032return result;1033}10341035statbuf->st_size = sv->sv_i.sfi_size;10361037/* We don't support these yet; you get to implement them */1038statbuf->st_nlink = 0;1039statbuf->st_blocks = 0;10401041/* Fill in other field as desired/possible... */10421043return 0;1044}10451046/*1047* Return the type of the file (types as per kern/stat.h)1048*/1049static1050int1051sfs_gettype(struct vnode *v, uint32_t *ret)1052{1053struct sfs_vnode *sv = v->vn_data;10541055vfs_biglock_acquire();10561057switch (sv->sv_i.sfi_type) {1058case SFS_TYPE_FILE:1059*ret = S_IFREG;1060vfs_biglock_release();1061return 0;1062case SFS_TYPE_DIR:1063*ret = S_IFDIR;1064vfs_biglock_release();1065return 0;1066}1067panic("sfs: gettype: Invalid inode type (inode %u, type %u)\n",1068sv->sv_ino, sv->sv_i.sfi_type);1069return EINVAL;1070}10711072/*1073* Check for legal seeks on files. Allow anything non-negative.1074* We could conceivably, here, prohibit seeking past the maximum1075* file size our inode structure can support, but we don't - few1076* people ever bother to check lseek() for failure and having1077* read() or write() fail is sufficient.1078*/1079static1080int1081sfs_tryseek(struct vnode *v, off_t pos)1082{1083if (pos<0) {1084return EINVAL;1085}10861087/* Allow anything else */1088(void)v;10891090return 0;1091}10921093/*1094* Called for fsync(), and also on filesystem unmount, global sync(),1095* and some other cases.1096*/1097static1098int1099sfs_fsync(struct vnode *v)1100{1101struct sfs_vnode *sv = v->vn_data;1102int result;11031104vfs_biglock_acquire();1105result = sfs_sync_inode(sv);1106vfs_biglock_release();11071108return result;1109}11101111/*1112* Called for mmap().1113*/1114static1115int1116sfs_mmap(struct vnode *v /* add stuff as needed */)1117{1118(void)v;1119return EUNIMP;1120}11211122/*1123* Called for ftruncate() and from sfs_reclaim.1124*/1125static1126int1127sfs_truncate(struct vnode *v, off_t len)1128{1129/*1130* I/O buffer for handling the indirect block.1131*1132* Note: in real life (and when you've done the fs assignment)1133* you would get space from the disk buffer cache for this,1134* not use a static area.1135*/1136static uint32_t idbuf[SFS_DBPERIDB];11371138struct sfs_vnode *sv = v->vn_data;1139struct sfs_fs *sfs = sv->sv_v.vn_fs->fs_data;11401141/* Length in blocks (divide rounding up) */1142uint32_t blocklen = DIVROUNDUP(len, SFS_BLOCKSIZE);11431144uint32_t i, j, block;1145uint32_t idblock, baseblock, highblock;1146int result;1147int hasnonzero, iddirty;11481149KASSERT(sizeof(idbuf)==SFS_BLOCKSIZE);11501151vfs_biglock_acquire();11521153/*1154* Go through the direct blocks. Discard any that are1155* past the limit we're truncating to.1156*/1157for (i=0; i<SFS_NDIRECT; i++) {1158block = sv->sv_i.sfi_direct[i];1159if (i >= blocklen && block != 0) {1160sfs_bfree(sfs, block);1161sv->sv_i.sfi_direct[i] = 0;1162sv->sv_dirty = true;1163}1164}11651166/* Indirect block number */1167idblock = sv->sv_i.sfi_indirect;11681169/* The lowest block in the indirect block */1170baseblock = SFS_NDIRECT;11711172/* The highest block in the indirect block */1173highblock = baseblock + SFS_DBPERIDB - 1;11741175if (blocklen < highblock && idblock != 0) {1176/* We're past the proposed EOF; may need to free stuff */11771178/* Read the indirect block */1179result = sfs_rblock(sfs, idbuf, idblock);1180if (result) {1181vfs_biglock_release();1182return result;1183}11841185hasnonzero = 0;1186iddirty = 0;1187for (j=0; j<SFS_DBPERIDB; j++) {1188/* Discard any blocks that are past the new EOF */1189if (blocklen < baseblock+j && idbuf[j] != 0) {1190sfs_bfree(sfs, idbuf[j]);1191idbuf[j] = 0;1192iddirty = 1;1193}1194/* Remember if we see any nonzero blocks in here */1195if (idbuf[j]!=0) {1196hasnonzero=1;1197}1198}11991200if (!hasnonzero) {1201/* The whole indirect block is empty now; free it */1202sfs_bfree(sfs, idblock);1203sv->sv_i.sfi_indirect = 0;1204sv->sv_dirty = true;1205}1206else if (iddirty) {1207/* The indirect block is dirty; write it back */1208result = sfs_wblock(sfs, idbuf, idblock);1209if (result) {1210vfs_biglock_release();1211return result;1212}1213}1214}12151216/* Set the file size */1217sv->sv_i.sfi_size = len;12181219/* Mark the inode dirty */1220sv->sv_dirty = true;12211222vfs_biglock_release();1223return 0;1224}12251226/*1227* Get the full pathname for a file. This only needs to work on directories.1228* Since we don't support subdirectories, assume it's the root directory1229* and hand back the empty string. (The VFS layer takes care of the1230* device name, leading slash, etc.)1231*/1232static1233int1234sfs_namefile(struct vnode *vv, struct uio *uio)1235{1236struct sfs_vnode *sv = vv->vn_data;1237KASSERT(sv->sv_ino == SFS_ROOT_LOCATION);12381239/* send back the empty string - just return */12401241(void)uio;12421243return 0;1244}12451246/*1247* Create a file. If EXCL is set, insist that the filename not already1248* exist; otherwise, if it already exists, just open it.1249*/1250static1251int1252sfs_creat(struct vnode *v, const char *name, bool excl, mode_t mode,1253struct vnode **ret)1254{1255struct sfs_fs *sfs = v->vn_fs->fs_data;1256struct sfs_vnode *sv = v->vn_data;1257struct sfs_vnode *newguy;1258uint32_t ino;1259int result;12601261vfs_biglock_acquire();12621263/* Look up the name */1264result = sfs_dir_findname(sv, name, &ino, NULL, NULL);1265if (result!=0 && result!=ENOENT) {1266vfs_biglock_release();1267return result;1268}12691270/* If it exists and we didn't want it to, fail */1271if (result==0 && excl) {1272vfs_biglock_release();1273return EEXIST;1274}12751276if (result==0) {1277/* We got a file; load its vnode and return */1278result = sfs_loadvnode(sfs, ino, SFS_TYPE_INVAL, &newguy);1279if (result) {1280vfs_biglock_release();1281return result;1282}1283*ret = &newguy->sv_v;1284vfs_biglock_release();1285return 0;1286}12871288/* Didn't exist - create it */1289result = sfs_makeobj(sfs, SFS_TYPE_FILE, &newguy);1290if (result) {1291vfs_biglock_release();1292return result;1293}12941295/* We don't currently support file permissions; ignore MODE */1296(void)mode;12971298/* Link it into the directory */1299result = sfs_dir_link(sv, name, newguy->sv_ino, NULL);1300if (result) {1301VOP_DECREF(&newguy->sv_v);1302vfs_biglock_release();1303return result;1304}13051306/* Update the linkcount of the new file */1307newguy->sv_i.sfi_linkcount++;13081309/* and consequently mark it dirty. */1310newguy->sv_dirty = true;13111312*ret = &newguy->sv_v;13131314vfs_biglock_release();1315return 0;1316}13171318/*1319* Make a hard link to a file.1320* The VFS layer should prevent this being called unless both1321* vnodes are ours.1322*/1323static1324int1325sfs_link(struct vnode *dir, const char *name, struct vnode *file)1326{1327struct sfs_vnode *sv = dir->vn_data;1328struct sfs_vnode *f = file->vn_data;1329int result;13301331KASSERT(file->vn_fs == dir->vn_fs);13321333vfs_biglock_acquire();13341335/* Just create a link */1336result = sfs_dir_link(sv, name, f->sv_ino, NULL);1337if (result) {1338vfs_biglock_release();1339return result;1340}13411342/* and update the link count, marking the inode dirty */1343f->sv_i.sfi_linkcount++;1344f->sv_dirty = true;13451346vfs_biglock_release();1347return 0;1348}13491350/*1351* Delete a file.1352*/1353static1354int1355sfs_remove(struct vnode *dir, const char *name)1356{1357struct sfs_vnode *sv = dir->vn_data;1358struct sfs_vnode *victim;1359int slot;1360int result;13611362vfs_biglock_acquire();13631364/* Look for the file and fetch a vnode for it. */1365result = sfs_lookonce(sv, name, &victim, &slot);1366if (result) {1367vfs_biglock_release();1368return result;1369}13701371/* Erase its directory entry. */1372result = sfs_dir_unlink(sv, slot);1373if (result==0) {1374/* If we succeeded, decrement the link count. */1375KASSERT(victim->sv_i.sfi_linkcount > 0);1376victim->sv_i.sfi_linkcount--;1377victim->sv_dirty = true;1378}13791380/* Discard the reference that sfs_lookonce got us */1381VOP_DECREF(&victim->sv_v);13821383vfs_biglock_release();1384return result;1385}13861387/*1388* Rename a file.1389*1390* Since we don't support subdirectories, assumes that the two1391* directories passed are the same.1392*/1393static1394int1395sfs_rename(struct vnode *d1, const char *n1,1396struct vnode *d2, const char *n2)1397{1398struct sfs_vnode *sv = d1->vn_data;1399struct sfs_vnode *g1;1400int slot1, slot2;1401int result, result2;14021403vfs_biglock_acquire();14041405KASSERT(d1==d2);1406KASSERT(sv->sv_ino == SFS_ROOT_LOCATION);14071408/* Look up the old name of the file and get its inode and slot number*/1409result = sfs_lookonce(sv, n1, &g1, &slot1);1410if (result) {1411vfs_biglock_release();1412return result;1413}14141415/* We don't support subdirectories */1416KASSERT(g1->sv_i.sfi_type == SFS_TYPE_FILE);14171418/*1419* Link it under the new name.1420*1421* We could theoretically just overwrite the original1422* directory entry, except that we need to check to make sure1423* the new name doesn't already exist; might as well use the1424* existing link routine.1425*/1426result = sfs_dir_link(sv, n2, g1->sv_ino, &slot2);1427if (result) {1428goto puke;1429}14301431/* Increment the link count, and mark inode dirty */1432g1->sv_i.sfi_linkcount++;1433g1->sv_dirty = true;14341435/* Unlink the old slot */1436result = sfs_dir_unlink(sv, slot1);1437if (result) {1438goto puke_harder;1439}14401441/*1442* Decrement the link count again, and mark the inode dirty again,1443* in case it's been synced behind our back.1444*/1445KASSERT(g1->sv_i.sfi_linkcount>0);1446g1->sv_i.sfi_linkcount--;1447g1->sv_dirty = true;14481449/* Let go of the reference to g1 */1450VOP_DECREF(&g1->sv_v);14511452vfs_biglock_release();1453return 0;14541455puke_harder:1456/*1457* Error recovery: try to undo what we already did1458*/1459result2 = sfs_dir_unlink(sv, slot2);1460if (result2) {1461kprintf("sfs: rename: %s\n", strerror(result));1462kprintf("sfs: rename: while cleaning up: %s\n",1463strerror(result2));1464panic("sfs: rename: Cannot recover\n");1465}1466g1->sv_i.sfi_linkcount--;1467puke:1468/* Let go of the reference to g1 */1469VOP_DECREF(&g1->sv_v);1470vfs_biglock_release();1471return result;1472}14731474/*1475* lookparent returns the last path component as a string and the1476* directory it's in as a vnode.1477*1478* Since we don't support subdirectories, this is very easy -1479* return the root dir and copy the path.1480*/1481static1482int1483sfs_lookparent(struct vnode *v, char *path, struct vnode **ret,1484char *buf, size_t buflen)1485{1486struct sfs_vnode *sv = v->vn_data;14871488vfs_biglock_acquire();14891490if (sv->sv_i.sfi_type != SFS_TYPE_DIR) {1491vfs_biglock_release();1492return ENOTDIR;1493}14941495if (strlen(path)+1 > buflen) {1496vfs_biglock_release();1497return ENAMETOOLONG;1498}1499strcpy(buf, path);15001501VOP_INCREF(&sv->sv_v);1502*ret = &sv->sv_v;15031504vfs_biglock_release();1505return 0;1506}15071508/*1509* Lookup gets a vnode for a pathname.1510*1511* Since we don't support subdirectories, it's easy - just look up the1512* name.1513*/1514static1515int1516sfs_lookup(struct vnode *v, char *path, struct vnode **ret)1517{1518struct sfs_vnode *sv = v->vn_data;1519struct sfs_vnode *final;1520int result;15211522vfs_biglock_acquire();15231524if (sv->sv_i.sfi_type != SFS_TYPE_DIR) {1525vfs_biglock_release();1526return ENOTDIR;1527}15281529result = sfs_lookonce(sv, path, &final, NULL);1530if (result) {1531vfs_biglock_release();1532return result;1533}15341535*ret = &final->sv_v;15361537vfs_biglock_release();1538return 0;1539}15401541//////////////////////////////////////////////////15421543static1544int1545sfs_notdir(void)1546{1547return ENOTDIR;1548}15491550static1551int1552sfs_isdir(void)1553{1554return EISDIR;1555}15561557static1558int1559sfs_unimp(void)1560{1561return EUNIMP;1562}15631564/*1565* Casting through void * prevents warnings.1566* All of the vnode ops return int, and it's ok to cast functions that1567* take args to functions that take no args.1568*/15691570#define ISDIR ((void *)sfs_isdir)1571#define NOTDIR ((void *)sfs_notdir)1572#define UNIMP ((void *)sfs_unimp)15731574/*1575* Function table for sfs files.1576*/1577static const struct vnode_ops sfs_fileops = {1578VOP_MAGIC, /* mark this a valid vnode ops table */15791580sfs_open,1581sfs_close,1582sfs_reclaim,15831584sfs_read,1585NOTDIR, /* readlink */1586NOTDIR, /* getdirentry */1587sfs_write,1588sfs_ioctl,1589sfs_stat,1590sfs_gettype,1591sfs_tryseek,1592sfs_fsync,1593sfs_mmap,1594sfs_truncate,1595NOTDIR, /* namefile */15961597NOTDIR, /* creat */1598NOTDIR, /* symlink */1599NOTDIR, /* mkdir */1600NOTDIR, /* link */1601NOTDIR, /* remove */1602NOTDIR, /* rmdir */1603NOTDIR, /* rename */16041605NOTDIR, /* lookup */1606NOTDIR, /* lookparent */1607};16081609/*1610* Function table for the sfs directory.1611*/1612static const struct vnode_ops sfs_dirops = {1613VOP_MAGIC, /* mark this a valid vnode ops table */16141615sfs_opendir,1616sfs_close,1617sfs_reclaim,16181619ISDIR, /* read */1620ISDIR, /* readlink */1621UNIMP, /* getdirentry */1622ISDIR, /* write */1623sfs_ioctl,1624sfs_stat,1625sfs_gettype,1626UNIMP, /* tryseek */1627sfs_fsync,1628ISDIR, /* mmap */1629ISDIR, /* truncate */1630sfs_namefile,16311632sfs_creat,1633UNIMP, /* symlink */1634UNIMP, /* mkdir */1635sfs_link,1636sfs_remove,1637UNIMP, /* rmdir */1638sfs_rename,16391640sfs_lookup,1641sfs_lookparent,1642};16431644/*1645* Function to load a inode into memory as a vnode, or dig up one1646* that's already resident.1647*/1648static1649int1650sfs_loadvnode(struct sfs_fs *sfs, uint32_t ino, int forcetype,1651struct sfs_vnode **ret)1652{1653struct vnode *v;1654struct sfs_vnode *sv;1655const struct vnode_ops *ops = NULL;1656unsigned i, num;1657int result;16581659/* Look in the vnodes table */1660num = vnodearray_num(sfs->sfs_vnodes);16611662/* Linear search. Is this too slow? You decide. */1663for (i=0; i<num; i++) {1664v = vnodearray_get(sfs->sfs_vnodes, i);1665sv = v->vn_data;16661667/* Every inode in memory must be in an allocated block */1668if (!sfs_bused(sfs, sv->sv_ino)) {1669panic("sfs: Found inode %u in unallocated block\n",1670sv->sv_ino);1671}16721673if (sv->sv_ino==ino) {1674/* Found */16751676/* May only be set when creating new objects */1677KASSERT(forcetype==SFS_TYPE_INVAL);16781679VOP_INCREF(&sv->sv_v);1680*ret = sv;1681return 0;1682}1683}16841685/* Didn't have it loaded; load it */16861687sv = kmalloc(sizeof(struct sfs_vnode));1688if (sv==NULL) {1689return ENOMEM;1690}16911692/* Must be in an allocated block */1693if (!sfs_bused(sfs, ino)) {1694panic("sfs: Tried to load inode %u from unallocated block\n",1695ino);1696}16971698/* Read the block the inode is in */1699result = sfs_rblock(sfs, &sv->sv_i, ino);1700if (result) {1701kfree(sv);1702return result;1703}17041705/* Not dirty yet */1706sv->sv_dirty = false;17071708/*1709* FORCETYPE is set if we're creating a new file, because the1710* block on disk will have been zeroed out and thus the type1711* recorded there will be SFS_TYPE_INVAL.1712*/1713if (forcetype != SFS_TYPE_INVAL) {1714KASSERT(sv->sv_i.sfi_type == SFS_TYPE_INVAL);1715sv->sv_i.sfi_type = forcetype;1716sv->sv_dirty = true;1717}17181719/*1720* Choose the function table based on the object type.1721*/1722switch (sv->sv_i.sfi_type) {1723case SFS_TYPE_FILE:1724ops = &sfs_fileops;1725break;1726case SFS_TYPE_DIR:1727ops = &sfs_dirops;1728break;1729default:1730panic("sfs: loadvnode: Invalid inode type "1731"(inode %u, type %u)\n",1732ino, sv->sv_i.sfi_type);1733}17341735/* Call the common vnode initializer */1736result = VOP_INIT(&sv->sv_v, ops, &sfs->sfs_absfs, sv);1737if (result) {1738kfree(sv);1739return result;1740}17411742/* Set the other fields in our vnode structure */1743sv->sv_ino = ino;17441745/* Add it to our table */1746result = vnodearray_add(sfs->sfs_vnodes, &sv->sv_v, NULL);1747if (result) {1748VOP_CLEANUP(&sv->sv_v);1749kfree(sv);1750return result;1751}17521753/* Hand it back */1754*ret = sv;1755return 0;1756}17571758/*1759* Get vnode for the root of the filesystem.1760* The root vnode is always found in block 1 (SFS_ROOT_LOCATION).1761*/1762struct vnode *1763sfs_getroot(struct fs *fs)1764{1765struct sfs_fs *sfs = fs->fs_data;1766struct sfs_vnode *sv;1767int result;17681769vfs_biglock_acquire();17701771result = sfs_loadvnode(sfs, SFS_ROOT_LOCATION, SFS_TYPE_INVAL, &sv);1772if (result) {1773panic("sfs: getroot: Cannot load root vnode\n");1774}17751776vfs_biglock_release();17771778return &sv->sv_v;1779}178017811782