860 lines
25 KiB
C
860 lines
25 KiB
C
/*
|
|
* Inode routines for 2.xBSD filesystem.
|
|
*
|
|
* Copyright (C) 2006-2011 Serge Vakulenko, <serge@vak.ru>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software
|
|
* and its documentation for any purpose and without fee is hereby
|
|
* granted, provided that the above copyright notice appear in all
|
|
* copies and that both that the copyright notice and this
|
|
* permission notice and warranty disclaimer appear in supporting
|
|
* documentation, and that the name of the author not be used in
|
|
* advertising or publicity pertaining to distribution of the
|
|
* software without specific, written prior permission.
|
|
*
|
|
* The author disclaim all warranties with regard to this
|
|
* software, including all implied warranties of merchantability
|
|
* and fitness. In no event shall the author be liable for any
|
|
* special, indirect or consequential damages or any damages
|
|
* whatsoever resulting from loss of use, data or profits, whether
|
|
* in an action of contract, negligence or other tortious action,
|
|
* arising out of or in connection with the use or performance of
|
|
* this software.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include "bsdfs.h"
|
|
|
|
extern int verbose;
|
|
|
|
int fs_inode_get (fs_t *fs, fs_inode_t *inode, unsigned inum)
|
|
{
|
|
unsigned long offset;
|
|
int i, reserved;
|
|
|
|
memset (inode, 0, sizeof (*inode));
|
|
inode->fs = fs;
|
|
inode->number = inum;
|
|
|
|
/* Inodes are numbered starting from 1.
|
|
* 64 bytes per inode, 16 inodes per block.
|
|
* Skip first block. */
|
|
if (inum == 0 || inum > (fs->isize-1) * BSDFS_INODES_PER_BLOCK)
|
|
return 0;
|
|
offset = (inode->number + BSDFS_INODES_PER_BLOCK - 1) *
|
|
BSDFS_BSIZE / BSDFS_INODES_PER_BLOCK;
|
|
|
|
if (! fs_seek (fs, offset))
|
|
return 0;
|
|
|
|
if (! fs_read16 (fs, &inode->mode)) /* file type and access mode */
|
|
return 0;
|
|
if (! fs_read16 (fs, &inode->nlink)) /* directory entries */
|
|
return 0;
|
|
if (! fs_read32 (fs, &inode->uid)) /* owner */
|
|
return 0;
|
|
if (! fs_read32 (fs, &inode->gid)) /* group */
|
|
return 0;
|
|
if (! fs_read32 (fs, (unsigned*) &inode->size)) /* size */
|
|
return 0;
|
|
|
|
for (i=0; i<NADDR; ++i) { /* device addresses constituting file */
|
|
if (! fs_read32 (fs, &inode->addr[i]))
|
|
return 0;
|
|
}
|
|
if (! fs_read32 (fs, (unsigned*) &reserved))
|
|
return 0;
|
|
if (! fs_read32 (fs, (unsigned*) &inode->flags))
|
|
return 0;
|
|
if (! fs_read32 (fs, (unsigned*) &inode->atime))
|
|
return 0; /* last access time */
|
|
if (! fs_read32 (fs, (unsigned*) &inode->mtime))
|
|
return 0; /* last modification time */
|
|
if (! fs_read32 (fs, (unsigned*) &inode->ctime))
|
|
return 0; /* creation time */
|
|
/*if (inode->mode) { fs_inode_print (inode, stdout); printf ("---\n"); }*/
|
|
if (verbose > 3)
|
|
printf ("get inode %u\n", inode->number);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Free all the disk blocks associated
|
|
* with the specified inode structure.
|
|
* The blocks of the file are removed
|
|
* in reverse order. This FILO
|
|
* algorithm will tend to maintain
|
|
* a contiguous free list much longer
|
|
* than FIFO.
|
|
*/
|
|
void fs_inode_truncate (fs_inode_t *inode, unsigned long size)
|
|
{
|
|
int i, nblk;
|
|
unsigned *blk;
|
|
|
|
if ((inode->mode & INODE_MODE_FMT) == INODE_MODE_FCHR ||
|
|
(inode->mode & INODE_MODE_FMT) == INODE_MODE_FBLK)
|
|
return;
|
|
|
|
#define SINGLE NDADDR /* index of single indirect block */
|
|
#define DOUBLE (SINGLE+1) /* index of double indirect block */
|
|
#define TRIPLE (DOUBLE+1) /* index of triple indirect block */
|
|
|
|
nblk = (size + BSDFS_BSIZE - 1) / BSDFS_BSIZE;
|
|
for (i=TRIPLE; i>=0; --i) {
|
|
blk = &inode->addr[i];
|
|
if (*blk == 0)
|
|
continue;
|
|
|
|
if (i == TRIPLE) {
|
|
if (! fs_triple_indirect_block_free (inode->fs, *blk,
|
|
nblk - (NDADDR + BSDFS_BSIZE/4 + BSDFS_BSIZE/4*BSDFS_BSIZE/4)))
|
|
break;
|
|
} else if (i == DOUBLE) {
|
|
if (! fs_double_indirect_block_free (inode->fs, *blk,
|
|
nblk - (NDADDR + BSDFS_BSIZE/4)))
|
|
break;
|
|
} else if (i == SINGLE) {
|
|
if (! fs_indirect_block_free (inode->fs, *blk, nblk - NDADDR))
|
|
break;
|
|
} else {
|
|
if (i * BSDFS_BSIZE < size)
|
|
break;
|
|
fs_block_free (inode->fs, *blk);
|
|
}
|
|
|
|
*blk = 0;
|
|
}
|
|
|
|
inode->size = size;
|
|
inode->dirty = 1;
|
|
}
|
|
|
|
void fs_inode_clear (fs_inode_t *inode)
|
|
{
|
|
inode->dirty = 1;
|
|
inode->mode = 0;
|
|
inode->nlink = 0;
|
|
inode->uid = 0;
|
|
inode->size = 0;
|
|
memset (inode->addr, 0, sizeof(inode->addr));
|
|
inode->atime = 0;
|
|
inode->mtime = 0;
|
|
}
|
|
|
|
int fs_inode_save (fs_inode_t *inode, int force)
|
|
{
|
|
unsigned long offset;
|
|
int i;
|
|
|
|
if (! inode->fs->writable)
|
|
return 0;
|
|
if (! force && ! inode->dirty)
|
|
return 1;
|
|
if (inode->number == 0 ||
|
|
inode->number > (inode->fs->isize-1) * BSDFS_INODES_PER_BLOCK)
|
|
return 0;
|
|
offset = (inode->number + BSDFS_INODES_PER_BLOCK - 1) *
|
|
BSDFS_BSIZE / BSDFS_INODES_PER_BLOCK;
|
|
|
|
time (&inode->atime);
|
|
//time (&inode->mtime);
|
|
|
|
if (! fs_seek (inode->fs, offset))
|
|
return 0;
|
|
|
|
if (! fs_write16 (inode->fs, inode->mode)) /* file type and access mode */
|
|
return 0;
|
|
if (! fs_write16 (inode->fs, inode->nlink)) /* directory entries */
|
|
return 0;
|
|
if (! fs_write32 (inode->fs, inode->uid)) /* owner */
|
|
return 0;
|
|
if (! fs_write32 (inode->fs, inode->gid)) /* group */
|
|
return 0;
|
|
if (! fs_write32 (inode->fs, inode->size)) /* size */
|
|
return 0;
|
|
|
|
for (i=0; i<NADDR; ++i) { /* device addresses constituting file */
|
|
if (! fs_write32 (inode->fs, inode->addr[i]))
|
|
return 0;
|
|
}
|
|
if (! fs_write32 (inode->fs, 0)) /* reserved */
|
|
return 0;
|
|
if (! fs_write32 (inode->fs, inode->flags)) /* flags */
|
|
return 0;
|
|
if (! fs_write32 (inode->fs, inode->atime)) /* last access time */
|
|
return 0;
|
|
if (! fs_write32 (inode->fs, inode->mtime)) /* last modification time */
|
|
return 0;
|
|
if (! fs_write32 (inode->fs, inode->ctime)) /* creation time */
|
|
return 0;
|
|
|
|
inode->dirty = 0;
|
|
if (verbose > 3)
|
|
printf ("save inode %u\n", inode->number);
|
|
return 1;
|
|
}
|
|
|
|
void fs_inode_print (fs_inode_t *inode, FILE *out)
|
|
{
|
|
int i;
|
|
|
|
fprintf (out, " I-node: %u\n", inode->number);
|
|
fprintf (out, " Type: %s\n",
|
|
(inode->mode & INODE_MODE_FMT) == INODE_MODE_FDIR ? "Directory" :
|
|
(inode->mode & INODE_MODE_FMT) == INODE_MODE_FCHR ? "Character device" :
|
|
(inode->mode & INODE_MODE_FMT) == INODE_MODE_FBLK ? "Block device" :
|
|
(inode->mode & INODE_MODE_FMT) == INODE_MODE_FREG ? "File" :
|
|
(inode->mode & INODE_MODE_FMT) == INODE_MODE_FLNK ? "Symbolic link" :
|
|
(inode->mode & INODE_MODE_FMT) == INODE_MODE_FSOCK? "Socket" :
|
|
"Unknown");
|
|
fprintf (out, " Size: %lu bytes\n", inode->size);
|
|
fprintf (out, " Mode: %#o\n", inode->mode);
|
|
|
|
fprintf (out, " ");
|
|
if (inode->mode & INODE_MODE_SUID) fprintf (out, " SUID");
|
|
if (inode->mode & INODE_MODE_SGID) fprintf (out, " SGID");
|
|
if (inode->mode & INODE_MODE_SVTX) fprintf (out, " SVTX");
|
|
if (inode->mode & INODE_MODE_READ) fprintf (out, " READ");
|
|
if (inode->mode & INODE_MODE_WRITE) fprintf (out, " WRITE");
|
|
if (inode->mode & INODE_MODE_EXEC) fprintf (out, " EXEC");
|
|
fprintf (out, "\n");
|
|
|
|
fprintf (out, " Links: %u\n", inode->nlink);
|
|
fprintf (out, " Owner id: %u\n", inode->uid);
|
|
|
|
fprintf (out, " Blocks:");
|
|
for (i=0; i<NADDR; ++i) {
|
|
fprintf (out, " %u", inode->addr[i]);
|
|
}
|
|
fprintf (out, "\n");
|
|
|
|
fprintf (out, " Created: %s", ctime (&inode->ctime));
|
|
fprintf (out, " Modified: %s", ctime (&inode->mtime));
|
|
fprintf (out, "Last access: %s", ctime (&inode->atime));
|
|
}
|
|
|
|
void fs_directory_scan (fs_inode_t *dir, char *dirname,
|
|
fs_directory_scanner_t scanner, void *arg)
|
|
{
|
|
fs_inode_t file;
|
|
unsigned long offset;
|
|
unsigned char name [BSDFS_BSIZE - 12];
|
|
struct {
|
|
unsigned int inum;
|
|
unsigned short reclen;
|
|
unsigned short namlen;
|
|
} dirent;
|
|
|
|
/* Variable record per file */
|
|
for (offset = 0; offset < dir->size; offset += dirent.reclen) {
|
|
if (! fs_inode_read (dir, offset, (unsigned char*) &dirent, sizeof(dirent))) {
|
|
fprintf (stderr, "%s: read error at offset %ld\n",
|
|
dirname[0] ? dirname : "/", offset);
|
|
return;
|
|
}
|
|
/*printf ("scan offset %lu: inum=%u, reclen=%u, namlen=%u\n", offset, dirent.inum, dirent.reclen, dirent.namlen);*/
|
|
if (! fs_inode_read (dir, offset+sizeof(dirent), name, (dirent.namlen + 4) / 4 * 4)) {
|
|
fprintf (stderr, "%s: name read error at offset %ld\n",
|
|
dirname[0] ? dirname : "/", offset);
|
|
return;
|
|
}
|
|
/*printf ("scan offset %lu: name='%s'\n", offset, name);*/
|
|
|
|
if (dirent.inum == 0 || (name[0]=='.' && name[1]==0) ||
|
|
(name[0]=='.' && name[1]=='.' && name[2]==0))
|
|
continue;
|
|
|
|
if (! fs_inode_get (dir->fs, &file, dirent.inum)) {
|
|
fprintf (stderr, "cannot scan inode %d\n", dirent.inum);
|
|
continue;
|
|
}
|
|
scanner (dir, &file, dirname, (char*) name, arg);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Return the physical block number on a device given the
|
|
* inode and the logical block number in a file.
|
|
*/
|
|
static unsigned map_block (fs_inode_t *inode, unsigned lbn)
|
|
{
|
|
unsigned block [BSDFS_BSIZE / 4];
|
|
unsigned int nb, i, j, sh;
|
|
|
|
/*
|
|
* Blocks 0..NADDR-4 are direct blocks.
|
|
*/
|
|
if (lbn < NADDR-3) {
|
|
/* small file algorithm */
|
|
return inode->addr [lbn];
|
|
}
|
|
|
|
/*
|
|
* Addresses NADDR-3, NADDR-2, and NADDR-1
|
|
* have single, double, triple indirect blocks.
|
|
* The first step is to determine
|
|
* how many levels of indirection.
|
|
*/
|
|
sh = 0;
|
|
nb = 1;
|
|
lbn -= NADDR-3;
|
|
for (j=3; ; j--) {
|
|
if (j == 0)
|
|
return 0;
|
|
sh += NSHIFT;
|
|
nb <<= NSHIFT;
|
|
if (lbn < nb)
|
|
break;
|
|
lbn -= nb;
|
|
}
|
|
|
|
/*
|
|
* Fetch the first indirect block.
|
|
*/
|
|
nb = inode->addr [NADDR-j];
|
|
if (nb == 0)
|
|
return 0;
|
|
|
|
/*
|
|
* Fetch through the indirect blocks.
|
|
*/
|
|
for(; j <= 3; j++) {
|
|
if (! fs_read_block (inode->fs, nb, (unsigned char*) block))
|
|
return 0;
|
|
|
|
sh -= NSHIFT;
|
|
i = (lbn >> sh) & NMASK;
|
|
nb = block [i];
|
|
if (nb == 0)
|
|
return 0;
|
|
}
|
|
return nb;
|
|
}
|
|
|
|
/*
|
|
* Bmap defines the structure of file system storage
|
|
* by returning the physical block number on a device given the
|
|
* inode and the logical block number in a file.
|
|
*/
|
|
static unsigned map_block_write (fs_inode_t *inode, unsigned lbn)
|
|
{
|
|
unsigned block [BSDFS_BSIZE / 4];
|
|
unsigned int nb, newb, sh, i, j;
|
|
|
|
/*
|
|
* Blocks 0..NADDR-3 are direct blocks.
|
|
*/
|
|
if (lbn < NADDR-3) {
|
|
/* small file algorithm */
|
|
nb = inode->addr [lbn];
|
|
if (nb != 0) {
|
|
if (verbose)
|
|
printf ("map logical block %d to physical %d\n", lbn, nb);
|
|
return nb;
|
|
}
|
|
|
|
/* allocate new block */
|
|
if (! fs_block_alloc (inode->fs, &nb))
|
|
return 0;
|
|
inode->addr[lbn] = nb;
|
|
inode->dirty = 1;
|
|
return nb;
|
|
}
|
|
|
|
/*
|
|
* Addresses NADDR-3, NADDR-2, and NADDR-1
|
|
* have single, double, triple indirect blocks.
|
|
* The first step is to determine
|
|
* how many levels of indirection.
|
|
*/
|
|
sh = 0;
|
|
nb = 1;
|
|
lbn -= NADDR-3;
|
|
for (j=3; ; j--) {
|
|
if (j == 0)
|
|
return 0;
|
|
sh += NSHIFT;
|
|
nb <<= NSHIFT;
|
|
if (lbn < nb)
|
|
break;
|
|
lbn -= nb;
|
|
}
|
|
|
|
/*
|
|
* Fetch the first indirect block.
|
|
*/
|
|
nb = inode->addr [NADDR-j];
|
|
if (nb == 0) {
|
|
if (! fs_block_alloc (inode->fs, &nb))
|
|
return 0;
|
|
if (verbose)
|
|
printf ("inode %d: allocate new block %d\n", inode->number, nb);
|
|
memset (block, 0, BSDFS_BSIZE);
|
|
if (! fs_write_block (inode->fs, nb, (unsigned char*) block))
|
|
return 0;
|
|
inode->addr [NADDR-j] = nb;
|
|
inode->dirty = 1;
|
|
}
|
|
|
|
/*
|
|
* Fetch through the indirect blocks
|
|
*/
|
|
for(; j <= 3; j++) {
|
|
if (! fs_read_block (inode->fs, nb, (unsigned char*) block))
|
|
return 0;
|
|
|
|
sh -= NSHIFT;
|
|
i = (lbn >> sh) & NMASK;
|
|
if (block [i] != 0)
|
|
nb = block [i];
|
|
else {
|
|
/* Allocate new block. */
|
|
if (! fs_block_alloc (inode->fs, &newb))
|
|
return 0;
|
|
if (verbose)
|
|
printf ("inode %d: allocate new block %d\n", inode->number, newb);
|
|
block[i] = newb;
|
|
if (! fs_write_block (inode->fs, nb, (unsigned char*) block))
|
|
return 0;
|
|
memset (block, 0, BSDFS_BSIZE);
|
|
if (! fs_write_block (inode->fs, newb, (unsigned char*) block))
|
|
return 0;
|
|
nb = newb;
|
|
}
|
|
}
|
|
return nb;
|
|
}
|
|
|
|
int fs_inode_read (fs_inode_t *inode, unsigned long offset,
|
|
unsigned char *data, unsigned long bytes)
|
|
{
|
|
unsigned char block [BSDFS_BSIZE];
|
|
unsigned long n;
|
|
unsigned int bn, inblock_offset;
|
|
|
|
if (bytes + offset > inode->size)
|
|
return 0;
|
|
while (bytes != 0) {
|
|
inblock_offset = offset % BSDFS_BSIZE;
|
|
n = BSDFS_BSIZE - inblock_offset;
|
|
if (n > bytes)
|
|
n = bytes;
|
|
|
|
bn = map_block (inode, offset / BSDFS_BSIZE);
|
|
if (bn == 0)
|
|
return 0;
|
|
|
|
if (! fs_read_block (inode->fs, bn, block))
|
|
return 0;
|
|
memcpy (data, block + inblock_offset, n);
|
|
data += n;
|
|
offset += n;
|
|
bytes -= n;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int fs_inode_write (fs_inode_t *inode, unsigned long offset,
|
|
unsigned char *data, unsigned long bytes)
|
|
{
|
|
unsigned char block [BSDFS_BSIZE];
|
|
unsigned long n;
|
|
unsigned int bn, inblock_offset;
|
|
|
|
time (&inode->mtime);
|
|
while (bytes != 0) {
|
|
inblock_offset = offset % BSDFS_BSIZE;
|
|
n = BSDFS_BSIZE - inblock_offset;
|
|
if (n > bytes)
|
|
n = bytes;
|
|
|
|
bn = map_block_write (inode, offset / BSDFS_BSIZE);
|
|
if (bn == 0)
|
|
return 0;
|
|
if (inode->size < offset + n) {
|
|
/* Increase file size. */
|
|
inode->size = offset + n;
|
|
inode->dirty = 1;
|
|
}
|
|
if (verbose)
|
|
printf ("inode %d offset %ld: write %ld bytes to block %d\n",
|
|
inode->number, offset, n, bn);
|
|
|
|
if (n == BSDFS_BSIZE) {
|
|
if (! fs_write_block (inode->fs, bn, data))
|
|
return 0;
|
|
} else {
|
|
if (! fs_read_block (inode->fs, bn, block))
|
|
return 0;
|
|
memcpy (block + inblock_offset, data, n);
|
|
if (! fs_write_block (inode->fs, bn, block))
|
|
return 0;
|
|
}
|
|
data += n;
|
|
offset += n;
|
|
bytes -= n;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Convert from dirent to raw data.
|
|
*/
|
|
void fs_dirent_pack (unsigned char *data, fs_dirent_t *dirent)
|
|
{
|
|
int i;
|
|
|
|
*data++ = dirent->ino;
|
|
*data++ = dirent->ino >> 8;
|
|
*data++ = dirent->ino >> 16;
|
|
*data++ = dirent->ino >> 24;
|
|
*data++ = dirent->reclen;
|
|
*data++ = dirent->reclen >> 8;
|
|
*data++ = dirent->namlen;
|
|
*data++ = dirent->namlen >> 8;
|
|
for (i=0; dirent->name[i]; ++i)
|
|
*data++ = dirent->name[i];
|
|
for (; i & 3; ++i)
|
|
*data++ = 0;
|
|
}
|
|
|
|
/*
|
|
* Read dirent from raw data.
|
|
*/
|
|
void fs_dirent_unpack (fs_dirent_t *dirent, unsigned char *data)
|
|
{
|
|
dirent->ino = *data++;
|
|
dirent->ino |= *data++ << 8;
|
|
dirent->ino |= *data++ << 16;
|
|
dirent->ino |= *data++ << 24;
|
|
dirent->reclen = *data++;
|
|
dirent->reclen |= *data++ << 8;
|
|
dirent->namlen = *data++;
|
|
dirent->namlen |= *data++ << 8;
|
|
memset (dirent->name, 0, sizeof (dirent->name));
|
|
memcpy (dirent->name, data, dirent->namlen);
|
|
}
|
|
|
|
/*
|
|
* Convert a pathname into a pointer to
|
|
* an inode. Note that the inode is locked.
|
|
*
|
|
* op = 0 if name is saught
|
|
* 1 if name is to be created, mode is given
|
|
* 2 if name is to be deleted
|
|
* 3 if name is to be linked, mode contains inode number
|
|
*
|
|
* Return 0 on any error.
|
|
* Return 1 when the inode was found.
|
|
* Return 2 when the inode was created/deleted/linked.
|
|
*/
|
|
#define LOOKUP 0 /* perform name lookup only */
|
|
#define CREATE 1 /* setup for file creation */
|
|
#define DELETE 2 /* setup for file deletion */
|
|
#define LINK 3 /* setup for link */
|
|
|
|
int fs_inode_by_name (fs_t *fs, fs_inode_t *inode, const char *name,
|
|
int op, int mode)
|
|
{
|
|
fs_inode_t dir;
|
|
int c, namlen, reclen;
|
|
const char *namptr;
|
|
unsigned long offset, last_offset;
|
|
struct {
|
|
unsigned int inum;
|
|
unsigned short reclen;
|
|
unsigned short namlen;
|
|
} dirent;
|
|
|
|
/* Start from root. */
|
|
if (! fs_inode_get (fs, &dir, BSDFS_ROOT_INODE)) {
|
|
fprintf (stderr, "inode_open(): cannot get root\n");
|
|
return 0;
|
|
}
|
|
c = *name++;
|
|
while (c == '/')
|
|
c = *name++;
|
|
if (! c && op != LOOKUP) {
|
|
/* Cannot write or delete root directory. */
|
|
return 0;
|
|
}
|
|
cloop:
|
|
/* Here inode contains pointer
|
|
* to last component matched. */
|
|
if (! c) {
|
|
*inode = dir;
|
|
return 1;
|
|
}
|
|
|
|
/* If there is another component,
|
|
* inode must be a directory. */
|
|
if ((dir.mode & INODE_MODE_FMT) != INODE_MODE_FDIR) {
|
|
return 0;
|
|
}
|
|
|
|
/* Gather up dir name into buffer. */
|
|
namptr = name - 1;
|
|
while (c && c != '/') {
|
|
c = *name++;
|
|
}
|
|
namlen = name - namptr - 1;
|
|
while (c == '/')
|
|
c = *name++;
|
|
|
|
/* Search a directory, variable record per file */
|
|
if (verbose > 2)
|
|
printf ("scan for '%.*s', %d bytes\n", namlen, namptr, namlen);
|
|
last_offset = 0;
|
|
for (offset = 0; offset < dir.size; last_offset = offset, offset += dirent.reclen) {
|
|
unsigned char fname [BSDFS_BSIZE - 12];
|
|
|
|
if (! fs_inode_read (&dir, offset, (unsigned char*) &dirent, sizeof(dirent))) {
|
|
fprintf (stderr, "inode %d: read error at offset %ld\n",
|
|
dir.number, offset);
|
|
return 0;
|
|
}
|
|
if (verbose > 2)
|
|
printf ("scan offset %lu: inum=%u, reclen=%u, namlen=%u\n", offset, dirent.inum, dirent.reclen, dirent.namlen);
|
|
if (dirent.inum == 0 || dirent.namlen != namlen)
|
|
continue;
|
|
if (! fs_inode_read (&dir, offset+sizeof(dirent), fname, namlen)) {
|
|
fprintf (stderr, "inode %d: name read error at offset %ld\n",
|
|
dir.number, offset);
|
|
return 0;
|
|
}
|
|
if (verbose > 2)
|
|
printf ("scan offset %lu: name='%.*s'\n", offset, namlen, fname);
|
|
if (strncmp (namptr, (char*) fname, namlen) == 0) {
|
|
/* Here a component matched in a directory.
|
|
* If there is more pathname, go back to
|
|
* cloop, otherwise return. */
|
|
if (op == DELETE && ! c) {
|
|
goto delete_file;
|
|
}
|
|
if (! fs_inode_get (fs, &dir, dirent.inum)) {
|
|
fprintf (stderr, "inode_open(): cannot get inode %d\n", dirent.inum);
|
|
return 0;
|
|
}
|
|
goto cloop;
|
|
}
|
|
}
|
|
/* If at the end of the directory, the search failed.
|
|
* Report what is appropriate as per flag. */
|
|
if (op == CREATE && ! c)
|
|
goto create_file;
|
|
if (op == LINK && ! c)
|
|
goto create_link;
|
|
return 0;
|
|
|
|
/*
|
|
* Make a new file, and return it's inode.
|
|
*/
|
|
create_file:
|
|
if (! fs_inode_alloc (fs, inode)) {
|
|
fprintf (stderr, "%s: cannot allocate inode\n", namptr);
|
|
return 0;
|
|
}
|
|
inode->dirty = 1;
|
|
inode->mode = mode & (07777 | INODE_MODE_FMT);
|
|
if ((inode->mode & INODE_MODE_FMT) == 0)
|
|
inode->mode |= INODE_MODE_FREG;
|
|
inode->nlink = 1;
|
|
inode->uid = 0;
|
|
inode->flags = 0;
|
|
time (&inode->ctime);
|
|
if ((inode->mode & INODE_MODE_FMT) == INODE_MODE_FDIR) {
|
|
/* Make link '.' */
|
|
struct {
|
|
unsigned int inum;
|
|
unsigned short reclen;
|
|
unsigned short namlen;
|
|
char name [4];
|
|
} dotent;
|
|
dotent.inum = inode->number;
|
|
dotent.reclen = BSDFS_BSIZE;
|
|
dotent.namlen = 1;
|
|
memcpy (dotent.name, ".\0\0\0", 4);
|
|
if (! fs_inode_write (inode, 0, (unsigned char*) &dotent, sizeof(dotent))) {
|
|
fprintf (stderr, "inode %d: write error at offset %ld\n",
|
|
inode->number, 0L);
|
|
return 0;
|
|
}
|
|
/* Increase file size. */
|
|
inode->size = BSDFS_BSIZE;
|
|
++inode->nlink;
|
|
}
|
|
if (! fs_inode_save (inode, 0)) {
|
|
fprintf (stderr, "%s: cannot save file inode\n", namptr);
|
|
return 0;
|
|
}
|
|
|
|
/* Write a directory entry. */
|
|
if (verbose > 2)
|
|
printf ("*** create file '%.*s', inode %d\n", namlen, namptr, inode->number);
|
|
reclen = dirent.reclen - 8 - (dirent.namlen + 4) / 4 * 4;
|
|
c = 8 + (namlen + 4) / 4 * 4;
|
|
if (reclen >= c) {
|
|
/* Enough space */
|
|
dirent.reclen -= reclen;
|
|
if (verbose > 2)
|
|
printf ("*** previous entry %u-%u-%u at offset %lu\n",
|
|
dirent.inum, dirent.reclen, dirent.namlen, last_offset);
|
|
if (! fs_inode_write (&dir, last_offset, (unsigned char*) &dirent, sizeof(dirent))) {
|
|
fprintf (stderr, "inode %d: write error at offset %ld\n",
|
|
dir.number, last_offset);
|
|
return 0;
|
|
}
|
|
} else {
|
|
/* No space, extend directory. */
|
|
if (verbose > 2)
|
|
printf ("*** extend dir, previous entry %u-%u-%u at offset %lu\n",
|
|
dirent.inum, dirent.reclen, dirent.namlen, last_offset);
|
|
reclen = BSDFS_BSIZE;
|
|
}
|
|
offset = last_offset + dirent.reclen;
|
|
dirent.inum = inode->number;
|
|
dirent.reclen = reclen;
|
|
dirent.namlen = namlen;
|
|
if (verbose > 2)
|
|
printf ("*** new entry %u-%u-%u at offset %lu\n", dirent.inum, dirent.reclen, dirent.namlen, offset);
|
|
if (! fs_inode_write (&dir, offset, (unsigned char*) &dirent, sizeof(dirent))) {
|
|
fprintf (stderr, "inode %d: write error at offset %ld\n",
|
|
dir.number, offset);
|
|
return 0;
|
|
}
|
|
if (verbose > 2)
|
|
printf ("*** name '%.*s' at offset %lu\n", namlen, namptr, offset+sizeof(dirent));
|
|
if (! fs_inode_write (&dir, offset+sizeof(dirent), (unsigned char*) namptr, namlen)) {
|
|
fprintf (stderr, "inode %d: write error at offset %ld\n",
|
|
dir.number, offset+sizeof(dirent));
|
|
return 0;
|
|
}
|
|
/* Align directory size. */
|
|
dir.size = (dir.size + BSDFS_BSIZE - 1) / BSDFS_BSIZE * BSDFS_BSIZE;
|
|
if (! fs_inode_save (&dir, 0)) {
|
|
fprintf (stderr, "%s: cannot save directory inode\n", namptr);
|
|
return 0;
|
|
}
|
|
return 2;
|
|
|
|
/*
|
|
* Delete file. Return inode of deleted file.
|
|
*/
|
|
delete_file:
|
|
if (verbose > 2)
|
|
printf ("*** delete inode %d\n", dirent.inum);
|
|
if (! fs_inode_get (fs, inode, dirent.inum)) {
|
|
fprintf (stderr, "%s: cannot get inode %d\n", namptr, dirent.inum);
|
|
return 0;
|
|
}
|
|
inode->dirty = 1;
|
|
inode->nlink--;
|
|
if (inode->nlink <= 0) {
|
|
fs_inode_truncate (inode, 0);
|
|
fs_inode_clear (inode);
|
|
if (inode->fs->ninode < NICINOD) {
|
|
inode->fs->inode [inode->fs->ninode++] = dirent.inum;
|
|
inode->fs->dirty = 1;
|
|
}
|
|
}
|
|
/* Extend previous entry to cover the empty space. */
|
|
reclen = dirent.reclen;
|
|
if (! fs_inode_read (&dir, last_offset, (unsigned char*) &dirent, sizeof(dirent))) {
|
|
fprintf (stderr, "inode %d: read error at offset %ld\n",
|
|
dir.number, last_offset);
|
|
return 0;
|
|
}
|
|
dirent.reclen += reclen;
|
|
if (! fs_inode_write (&dir, last_offset, (unsigned char*) &dirent, sizeof(dirent))) {
|
|
fprintf (stderr, "inode %d: write error at offset %ld\n",
|
|
dir.number, last_offset);
|
|
return 0;
|
|
}
|
|
if (! fs_inode_save (&dir, 0)) {
|
|
fprintf (stderr, "%s: cannot save directory inode\n", namptr);
|
|
return 0;
|
|
}
|
|
return 2;
|
|
|
|
/*
|
|
* Make a link. Return a directory inode.
|
|
*/
|
|
create_link:
|
|
if (verbose > 2)
|
|
printf ("*** link inode %d to '%.*s', directory %d\n", mode, namlen, namptr, dir.number);
|
|
reclen = dirent.reclen - 8 - (dirent.namlen + 4) / 4 * 4;
|
|
dirent.reclen -= reclen;
|
|
if (verbose > 2)
|
|
printf ("*** previous entry %u-%u-%u at offset %lu\n", dirent.inum, dirent.reclen, dirent.namlen, last_offset);
|
|
if (! fs_inode_write (&dir, last_offset, (unsigned char*) &dirent, sizeof(dirent))) {
|
|
fprintf (stderr, "inode %d: write error at offset %ld\n",
|
|
dir.number, last_offset);
|
|
return 0;
|
|
}
|
|
offset = last_offset + dirent.reclen;
|
|
dirent.inum = mode;
|
|
dirent.reclen = reclen;
|
|
dirent.namlen = namlen;
|
|
if (verbose > 2)
|
|
printf ("*** new entry %u-%u-%u at offset %lu\n", dirent.inum, dirent.reclen, dirent.namlen, offset);
|
|
if (! fs_inode_write (&dir, offset, (unsigned char*) &dirent, sizeof(dirent))) {
|
|
fprintf (stderr, "inode %d: write error at offset %ld\n",
|
|
dir.number, offset);
|
|
return 0;
|
|
}
|
|
if (verbose > 2)
|
|
printf ("*** name '%.*s' at offset %lu\n", namlen, namptr, offset+sizeof(dirent));
|
|
if (! fs_inode_write (&dir, offset+sizeof(dirent), (unsigned char*) namptr, namlen)) {
|
|
fprintf (stderr, "inode %d: write error at offset %ld\n",
|
|
dir.number, offset+sizeof(dirent));
|
|
return 0;
|
|
}
|
|
if (! fs_inode_save (&dir, 0)) {
|
|
fprintf (stderr, "%s: cannot save directory inode\n", namptr);
|
|
return 0;
|
|
}
|
|
*inode = dir;
|
|
return 2;
|
|
}
|
|
|
|
/*
|
|
* Allocate an unused I node on the specified device.
|
|
* Used with file creation. The algorithm keeps up to
|
|
* NICINOD spare I nodes in the super block.
|
|
* When this runs out, a linear search through the
|
|
* I list is instituted to pick up NICINOD more.
|
|
*/
|
|
int fs_inode_alloc (fs_t *fs, fs_inode_t *inode)
|
|
{
|
|
int ino;
|
|
|
|
for (;;) {
|
|
if (fs->ninode <= 0) {
|
|
/* Build a list of free inodes. */
|
|
if (! inode_build_list (fs)) {
|
|
fprintf (stderr, "inode_alloc: cannot build inode list\n");
|
|
return 0;
|
|
}
|
|
if (fs->ninode <= 0) {
|
|
fprintf (stderr, "inode_alloc: out of in-core inodes\n");
|
|
return 0;
|
|
}
|
|
}
|
|
ino = fs->inode[--fs->ninode];
|
|
fs->dirty = 1;
|
|
fs->tinode--;
|
|
if (! fs_inode_get (fs, inode, ino)) {
|
|
fprintf (stderr, "inode_alloc: cannot get inode %d\n", ino);
|
|
return 0;
|
|
}
|
|
if (inode->mode == 0) {
|
|
fs_inode_clear (inode);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|