2013-12-06 12:04:52 +01:00
|
|
|
/* $NetBSD: dtfs_subr.c,v 1.4 2013/10/19 17:45:00 christos Exp $ */
|
2013-02-26 09:24:42 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Copyright (c) 2006 Antti Kantee. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
|
|
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <err.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <puffs.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <util.h>
|
|
|
|
|
|
|
|
#include "dtfs.h"
|
|
|
|
|
|
|
|
void
|
|
|
|
dtfs_baseattrs(struct vattr *vap, enum vtype type, ino_t id)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
struct timespec ts;
|
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
TIMEVAL_TO_TIMESPEC(&tv, &ts);
|
|
|
|
|
|
|
|
vap->va_type = type;
|
|
|
|
if (type == VDIR) {
|
|
|
|
vap->va_mode = 0777;
|
|
|
|
vap->va_nlink = 1; /* n + 1 after adding dent */
|
|
|
|
} else {
|
|
|
|
vap->va_mode = 0666;
|
|
|
|
vap->va_nlink = 0; /* n + 1 */
|
|
|
|
}
|
|
|
|
vap->va_uid = 0;
|
|
|
|
vap->va_gid = 0;
|
|
|
|
vap->va_fileid = id;
|
|
|
|
vap->va_size = 0;
|
|
|
|
vap->va_blocksize = getpagesize();
|
|
|
|
vap->va_gen = random();
|
|
|
|
vap->va_flags = 0;
|
|
|
|
vap->va_rdev = PUFFS_VNOVAL;
|
|
|
|
vap->va_bytes = 0;
|
|
|
|
vap->va_filerev = 1;
|
|
|
|
vap->va_vaflags = 0;
|
|
|
|
|
|
|
|
vap->va_atime = vap->va_mtime = vap->va_ctime = vap->va_birthtime = ts;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Well, as you can probably see, this interface has the slight problem
|
|
|
|
* of assuming file creation will always be succesful, or at least not
|
|
|
|
* giving a reason for the failure. Be sure to do better when you
|
|
|
|
* implement your own fs.
|
|
|
|
*/
|
|
|
|
struct puffs_node *
|
|
|
|
dtfs_genfile(struct puffs_node *dir, const struct puffs_cn *pcn,
|
|
|
|
enum vtype type)
|
|
|
|
{
|
2013-12-06 12:04:52 +01:00
|
|
|
struct dtfs_file *dff;
|
2013-02-26 09:24:42 +01:00
|
|
|
struct dtfs_dirent *dfd;
|
|
|
|
struct dtfs_mount *dtm;
|
|
|
|
struct puffs_node *newpn;
|
|
|
|
uid_t uid;
|
|
|
|
int rv;
|
|
|
|
|
|
|
|
assert(dir->pn_va.va_type == VDIR);
|
|
|
|
assert(dir->pn_mnt != NULL);
|
|
|
|
|
|
|
|
uid = 0;
|
|
|
|
rv = puffs_cred_getuid(pcn->pcn_cred, &uid);
|
|
|
|
assert(rv == 0);
|
|
|
|
|
|
|
|
if (type == VDIR) {
|
|
|
|
dff = dtfs_newdir();
|
|
|
|
dff->df_dotdot = dir;
|
|
|
|
} else
|
|
|
|
dff = dtfs_newfile();
|
|
|
|
|
|
|
|
dtm = puffs_pn_getmntspecific(dir);
|
|
|
|
newpn = puffs_pn_new(dir->pn_mnt, dff);
|
|
|
|
if (newpn == NULL)
|
|
|
|
errx(1, "getnewpnode");
|
|
|
|
dtfs_baseattrs(&newpn->pn_va, type, dtm->dtm_nextfileid++);
|
|
|
|
|
|
|
|
dfd = emalloc(sizeof(struct dtfs_dirent));
|
|
|
|
dfd->dfd_node = newpn;
|
|
|
|
dfd->dfd_name = estrndup(pcn->pcn_name, pcn->pcn_namelen);
|
|
|
|
dfd->dfd_namelen = strlen(dfd->dfd_name);
|
|
|
|
dfd->dfd_parent = dir;
|
|
|
|
dtfs_adddent(dir, dfd);
|
|
|
|
|
|
|
|
newpn->pn_va.va_uid = uid;
|
|
|
|
newpn->pn_va.va_gid = dir->pn_va.va_gid;
|
|
|
|
|
|
|
|
return newpn;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct dtfs_file *
|
|
|
|
dtfs_newdir()
|
|
|
|
{
|
|
|
|
struct dtfs_file *dff;
|
|
|
|
|
|
|
|
dff = emalloc(sizeof(struct dtfs_file));
|
|
|
|
memset(dff, 0, sizeof(struct dtfs_file));
|
|
|
|
LIST_INIT(&dff->df_dirents);
|
|
|
|
|
|
|
|
return dff;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct dtfs_file *
|
|
|
|
dtfs_newfile()
|
|
|
|
{
|
|
|
|
struct dtfs_file *dff;
|
|
|
|
|
|
|
|
dff = emalloc(sizeof(struct dtfs_file));
|
|
|
|
memset(dff, 0, sizeof(struct dtfs_file));
|
|
|
|
|
|
|
|
return dff;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct dtfs_dirent *
|
|
|
|
dtfs_dirgetnth(struct dtfs_file *searchdir, int n)
|
|
|
|
{
|
|
|
|
struct dtfs_dirent *dirent;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
LIST_FOREACH(dirent, &searchdir->df_dirents, dfd_entries) {
|
|
|
|
if (i == n)
|
|
|
|
return dirent;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct dtfs_dirent *
|
|
|
|
dtfs_dirgetbyname(struct dtfs_file *searchdir, const char *fname, size_t fnlen)
|
|
|
|
{
|
|
|
|
struct dtfs_dirent *dirent;
|
|
|
|
|
|
|
|
LIST_FOREACH(dirent, &searchdir->df_dirents, dfd_entries)
|
|
|
|
if (dirent->dfd_namelen == fnlen
|
|
|
|
&& strncmp(dirent->dfd_name, fname, fnlen) == 0)
|
|
|
|
return dirent;
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* common nuke, kill dirent from parent node
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
dtfs_nukenode(struct puffs_node *nukeme, struct puffs_node *pn_parent,
|
|
|
|
const char *fname, size_t fnlen)
|
|
|
|
{
|
|
|
|
struct dtfs_dirent *dfd;
|
|
|
|
struct dtfs_mount *dtm;
|
|
|
|
|
|
|
|
assert(pn_parent->pn_va.va_type == VDIR);
|
|
|
|
|
|
|
|
dfd = dtfs_dirgetbyname(DTFS_PTOF(pn_parent), fname, fnlen);
|
|
|
|
assert(dfd);
|
|
|
|
|
|
|
|
dtm = puffs_pn_getmntspecific(nukeme);
|
|
|
|
dtm->dtm_nfiles--;
|
|
|
|
assert(dtm->dtm_nfiles >= 1);
|
|
|
|
|
|
|
|
dtfs_removedent(pn_parent, dfd);
|
|
|
|
free(dfd);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* free lingering information */
|
|
|
|
void
|
|
|
|
dtfs_freenode(struct puffs_node *pn)
|
|
|
|
{
|
|
|
|
struct dtfs_file *df = DTFS_PTOF(pn);
|
|
|
|
struct dtfs_mount *dtm;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
assert(pn->pn_va.va_nlink == 0);
|
|
|
|
dtm = puffs_pn_getmntspecific(pn);
|
|
|
|
|
|
|
|
switch (pn->pn_va.va_type) {
|
|
|
|
case VREG:
|
|
|
|
assert(dtm->dtm_fsizes >= pn->pn_va.va_size);
|
|
|
|
dtm->dtm_fsizes -= pn->pn_va.va_size;
|
|
|
|
for (i = 0; i < BLOCKNUM(df->df_datalen, DTFS_BLOCKSHIFT); i++)
|
|
|
|
free(df->df_blocks[i]);
|
|
|
|
if (df->df_datalen > i << DTFS_BLOCKSHIFT)
|
|
|
|
free(df->df_blocks[i]);
|
|
|
|
break;
|
|
|
|
case VLNK:
|
|
|
|
free(df->df_linktarget);
|
|
|
|
break;
|
|
|
|
case VCHR:
|
|
|
|
case VBLK:
|
|
|
|
case VDIR:
|
|
|
|
case VSOCK:
|
|
|
|
case VFIFO:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(df);
|
|
|
|
puffs_pn_put(pn);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dtfs_setsize(struct puffs_node *pn, off_t newsize)
|
|
|
|
{
|
|
|
|
struct dtfs_file *df = DTFS_PTOF(pn);
|
|
|
|
struct dtfs_mount *dtm;
|
|
|
|
size_t newblocks;
|
|
|
|
int needalloc, shrinks;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
needalloc = newsize > ROUNDUP(df->df_datalen, DTFS_BLOCKSIZE);
|
|
|
|
shrinks = newsize < pn->pn_va.va_size;
|
|
|
|
|
|
|
|
if (needalloc || shrinks) {
|
|
|
|
newblocks = BLOCKNUM(newsize, DTFS_BLOCKSHIFT) + 1;
|
|
|
|
|
|
|
|
if (shrinks)
|
|
|
|
for (i = newblocks; i < df->df_numblocks; i++)
|
|
|
|
free(df->df_blocks[i]);
|
|
|
|
|
|
|
|
df->df_blocks = erealloc(df->df_blocks,
|
|
|
|
newblocks * sizeof(uint8_t *));
|
|
|
|
/*
|
|
|
|
* if extended, set storage to zero
|
|
|
|
* to match correct behaviour
|
|
|
|
*/
|
|
|
|
if (!shrinks) {
|
|
|
|
for (i = df->df_numblocks; i < newblocks; i++) {
|
|
|
|
df->df_blocks[i] = emalloc(DTFS_BLOCKSIZE);
|
|
|
|
memset(df->df_blocks[i], 0, DTFS_BLOCKSIZE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
df->df_datalen = newsize;
|
|
|
|
df->df_numblocks = newblocks;
|
|
|
|
}
|
|
|
|
|
|
|
|
dtm = puffs_pn_getmntspecific(pn);
|
|
|
|
if (!shrinks) {
|
|
|
|
dtm->dtm_fsizes += newsize - pn->pn_va.va_size;
|
|
|
|
} else {
|
|
|
|
dtm->dtm_fsizes -= pn->pn_va.va_size - newsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
pn->pn_va.va_size = newsize;
|
|
|
|
pn->pn_va.va_bytes = BLOCKNUM(newsize,DTFS_BLOCKSHIFT)>>DTFS_BLOCKSHIFT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add & bump link count */
|
|
|
|
void
|
|
|
|
dtfs_adddent(struct puffs_node *pn_dir, struct dtfs_dirent *dent)
|
|
|
|
{
|
|
|
|
struct dtfs_file *dir = DTFS_PTOF(pn_dir);
|
|
|
|
struct puffs_node *pn_file = dent->dfd_node;
|
|
|
|
struct dtfs_file *file = DTFS_PTOF(pn_file);
|
|
|
|
struct dtfs_mount *dtm;
|
|
|
|
|
|
|
|
assert(pn_dir->pn_va.va_type == VDIR);
|
|
|
|
LIST_INSERT_HEAD(&dir->df_dirents, dent, dfd_entries);
|
|
|
|
pn_file->pn_va.va_nlink++;
|
|
|
|
|
|
|
|
dtm = puffs_pn_getmntspecific(pn_file);
|
|
|
|
dtm->dtm_nfiles++;
|
|
|
|
|
|
|
|
dent->dfd_parent = pn_dir;
|
|
|
|
if (dent->dfd_node->pn_va.va_type == VDIR) {
|
|
|
|
file->df_dotdot = pn_dir;
|
|
|
|
pn_dir->pn_va.va_nlink++;
|
|
|
|
}
|
|
|
|
|
|
|
|
dtfs_updatetimes(pn_dir, 0, 1, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remove & lower link count */
|
|
|
|
void
|
|
|
|
dtfs_removedent(struct puffs_node *pn_dir, struct dtfs_dirent *dent)
|
|
|
|
{
|
|
|
|
struct puffs_node *pn_file = dent->dfd_node;
|
|
|
|
|
|
|
|
assert(pn_dir->pn_va.va_type == VDIR);
|
|
|
|
LIST_REMOVE(dent, dfd_entries);
|
|
|
|
if (pn_file->pn_va.va_type == VDIR) {
|
|
|
|
struct dtfs_file *df = DTFS_PTOF(pn_file);
|
|
|
|
|
|
|
|
pn_dir->pn_va.va_nlink--;
|
|
|
|
df->df_dotdot = NULL;
|
|
|
|
}
|
|
|
|
pn_file->pn_va.va_nlink--;
|
|
|
|
assert(pn_dir->pn_va.va_nlink >= 2);
|
|
|
|
|
|
|
|
dtfs_updatetimes(pn_dir, 0, 1, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
dtfs_updatetimes(struct puffs_node *pn, int doatime, int doctime, int domtime)
|
|
|
|
{
|
|
|
|
struct timeval tv;
|
|
|
|
struct timespec ts;
|
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
TIMEVAL_TO_TIMESPEC(&tv, &ts);
|
|
|
|
|
|
|
|
if (doatime)
|
|
|
|
pn->pn_va.va_atime = ts;
|
|
|
|
if (doctime)
|
|
|
|
pn->pn_va.va_ctime = ts;
|
|
|
|
if (domtime)
|
|
|
|
pn->pn_va.va_mtime = ts;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
dtfs_isunder(struct puffs_node *pn, struct puffs_node *pn_parent)
|
|
|
|
{
|
|
|
|
struct dtfs_file *df;
|
|
|
|
|
|
|
|
while (pn) {
|
|
|
|
if (pn == pn_parent)
|
|
|
|
return true;
|
|
|
|
df = DTFS_CTOF(pn);
|
|
|
|
pn = df->df_dotdot;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|