version 1.46, 2010/06/26 03:38:14 |
version 1.46.2.1, 2011/06/06 09:09:24 |
Line 51 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 51 __KERNEL_RCSID(0, "$NetBSD$"); |
|
#include <sys/stat.h> |
#include <sys/stat.h> |
#include <sys/systm.h> |
#include <sys/systm.h> |
#include <sys/vnode.h> |
#include <sys/vnode.h> |
#include <sys/proc.h> |
|
#include <sys/module.h> |
#include <sys/module.h> |
|
|
#include <miscfs/genfs/genfs.h> |
#include <miscfs/genfs/genfs.h> |
Line 60 __KERNEL_RCSID(0, "$NetBSD$"); |
|
Line 59 __KERNEL_RCSID(0, "$NetBSD$"); |
|
|
|
MODULE(MODULE_CLASS_VFS, tmpfs, NULL); |
MODULE(MODULE_CLASS_VFS, tmpfs, NULL); |
|
|
/* --------------------------------------------------------------------- */ |
struct pool tmpfs_dirent_pool; |
|
struct pool tmpfs_node_pool; |
|
|
static int tmpfs_mount(struct mount *, const char *, void *, size_t *); |
static int tmpfs_mount(struct mount *, const char *, void *, size_t *); |
static int tmpfs_start(struct mount *, int); |
static int tmpfs_start(struct mount *, int); |
static int tmpfs_unmount(struct mount *, int); |
static int tmpfs_unmount(struct mount *, int); |
static int tmpfs_root(struct mount *, struct vnode **); |
static int tmpfs_root(struct mount *, vnode_t **); |
static int tmpfs_vget(struct mount *, ino_t, struct vnode **); |
static int tmpfs_vget(struct mount *, ino_t, vnode_t **); |
static int tmpfs_fhtovp(struct mount *, struct fid *, struct vnode **); |
static int tmpfs_fhtovp(struct mount *, struct fid *, vnode_t **); |
static int tmpfs_vptofh(struct vnode *, struct fid *, size_t *); |
static int tmpfs_vptofh(struct vnode *, struct fid *, size_t *); |
static int tmpfs_statvfs(struct mount *, struct statvfs *); |
static int tmpfs_statvfs(struct mount *, struct statvfs *); |
static int tmpfs_sync(struct mount *, int, kauth_cred_t); |
static int tmpfs_sync(struct mount *, int, kauth_cred_t); |
static void tmpfs_init(void); |
static void tmpfs_init(void); |
static void tmpfs_done(void); |
static void tmpfs_done(void); |
static int tmpfs_snapshot(struct mount *, struct vnode *, |
static int tmpfs_snapshot(struct mount *, vnode_t *, struct timespec *); |
struct timespec *); |
|
|
|
/* --------------------------------------------------------------------- */ |
static void |
|
tmpfs_init(void) |
|
{ |
|
|
|
pool_init(&tmpfs_dirent_pool, sizeof(tmpfs_dirent_t), 0, 0, 0, |
|
"tmpfs_dirent", &pool_allocator_nointr, IPL_NONE); |
|
pool_init(&tmpfs_node_pool, sizeof(tmpfs_node_t), 0, 0, 0, |
|
"tmpfs_node", &pool_allocator_nointr, IPL_NONE); |
|
} |
|
|
|
static void |
|
tmpfs_done(void) |
|
{ |
|
|
|
pool_destroy(&tmpfs_dirent_pool); |
|
pool_destroy(&tmpfs_node_pool); |
|
} |
|
|
static int |
static int |
tmpfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) |
tmpfs_mount(struct mount *mp, const char *path, void *data, size_t *data_len) |
{ |
{ |
struct tmpfs_mount *tmp; |
|
struct tmpfs_node *root; |
|
struct tmpfs_args *args = data; |
struct tmpfs_args *args = data; |
|
tmpfs_mount_t *tmp; |
|
tmpfs_node_t *root; |
uint64_t memlimit; |
uint64_t memlimit; |
ino_t nodes; |
ino_t nodes; |
int error; |
int error; |
|
|
if (*data_len < sizeof *args) |
/* Validate the version. */ |
|
if (*data_len < sizeof(*args) || |
|
args->ta_version != TMPFS_ARGS_VERSION) |
return EINVAL; |
return EINVAL; |
|
|
/* Handle retrieval of mount point arguments. */ |
/* Handle retrieval of mount point arguments. */ |
Line 111 tmpfs_mount(struct mount *mp, const char |
|
Line 128 tmpfs_mount(struct mount *mp, const char |
|
} |
} |
|
|
if (mp->mnt_flag & MNT_UPDATE) { |
if (mp->mnt_flag & MNT_UPDATE) { |
/* XXX: There is no support yet to update file system |
/* TODO */ |
* settings. Should be added. */ |
|
|
|
return EOPNOTSUPP; |
return EOPNOTSUPP; |
} |
} |
|
|
if (args->ta_version != TMPFS_ARGS_VERSION) |
/* Prohibit mounts if there is not enough memory. */ |
return EINVAL; |
|
|
|
/* Do not allow mounts if we do not have enough memory to preserve |
|
* the minimum reserved pages. */ |
|
if (tmpfs_mem_info(true) < TMPFS_PAGES_RESERVED) |
if (tmpfs_mem_info(true) < TMPFS_PAGES_RESERVED) |
return EINVAL; |
return EINVAL; |
|
|
Line 142 tmpfs_mount(struct mount *mp, const char |
|
Line 153 tmpfs_mount(struct mount *mp, const char |
|
KASSERT(nodes >= 3); |
KASSERT(nodes >= 3); |
|
|
/* Allocate the tmpfs mount structure and fill it. */ |
/* Allocate the tmpfs mount structure and fill it. */ |
tmp = kmem_alloc(sizeof(struct tmpfs_mount), KM_SLEEP); |
tmp = kmem_zalloc(sizeof(tmpfs_mount_t), KM_SLEEP); |
if (tmp == NULL) |
if (tmp == NULL) |
return ENOMEM; |
return ENOMEM; |
|
|
Line 155 tmpfs_mount(struct mount *mp, const char |
|
Line 166 tmpfs_mount(struct mount *mp, const char |
|
|
|
/* Allocate the root node. */ |
/* Allocate the root node. */ |
error = tmpfs_alloc_node(tmp, VDIR, args->ta_root_uid, |
error = tmpfs_alloc_node(tmp, VDIR, args->ta_root_uid, |
args->ta_root_gid, args->ta_root_mode & ALLPERMS, NULL, NULL, |
args->ta_root_gid, args->ta_root_mode & ALLPERMS, NULL, |
VNOVAL, &root); |
VNOVAL, &root); |
KASSERT(error == 0 && root != NULL); |
KASSERT(error == 0 && root != NULL); |
|
|
|
/* |
|
* Parent of the root inode is itself. Also, root inode has no |
|
* directory entry (i.e. is never attached), thus hold an extra |
|
* reference (link) for it. |
|
*/ |
root->tn_links++; |
root->tn_links++; |
|
root->tn_spec.tn_dir.tn_parent = root; |
tmp->tm_root = root; |
tmp->tm_root = root; |
|
|
mp->mnt_data = tmp; |
mp->mnt_data = tmp; |
Line 169 tmpfs_mount(struct mount *mp, const char |
|
Line 187 tmpfs_mount(struct mount *mp, const char |
|
mp->mnt_iflag |= IMNT_MPSAFE; |
mp->mnt_iflag |= IMNT_MPSAFE; |
vfs_getnewfsid(mp); |
vfs_getnewfsid(mp); |
|
|
return set_statvfs_info(path, UIO_USERSPACE, "tmpfs", UIO_SYSSPACE, |
error = set_statvfs_info(path, UIO_USERSPACE, "tmpfs", UIO_SYSSPACE, |
mp->mnt_op->vfs_name, mp, curlwp); |
mp->mnt_op->vfs_name, mp, curlwp); |
|
if (error) { |
|
(void)tmpfs_unmount(mp, MNT_FORCE); |
|
} |
|
return error; |
} |
} |
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
static int |
static int |
tmpfs_start(struct mount *mp, int flags) |
tmpfs_start(struct mount *mp, int flags) |
{ |
{ |
Line 182 tmpfs_start(struct mount *mp, int flags) |
|
Line 202 tmpfs_start(struct mount *mp, int flags) |
|
return 0; |
return 0; |
} |
} |
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
/* ARGSUSED2 */ |
|
static int |
static int |
tmpfs_unmount(struct mount *mp, int mntflags) |
tmpfs_unmount(struct mount *mp, int mntflags) |
{ |
{ |
int error; |
tmpfs_mount_t *tmp; |
int flags = 0; |
tmpfs_node_t *node; |
struct tmpfs_mount *tmp; |
int error, flags = 0; |
struct tmpfs_node *node; |
|
|
|
/* Handle forced unmounts. */ |
/* Handle forced unmounts. */ |
if (mntflags & MNT_FORCE) |
if (mntflags & MNT_FORCE) |
Line 204 tmpfs_unmount(struct mount *mp, int mntf |
|
Line 220 tmpfs_unmount(struct mount *mp, int mntf |
|
|
|
tmp = VFS_TO_TMPFS(mp); |
tmp = VFS_TO_TMPFS(mp); |
|
|
/* Free all associated data. The loop iterates over the linked list |
/* Destroy any existing inodes. */ |
* we have containing all used nodes. For each of them that is |
while ((node = LIST_FIRST(&tmp->tm_nodes)) != NULL) { |
* a directory, we free all its directory entries. Note that after |
|
* freeing a node, it will automatically go to the available list, |
|
* so we will later have to iterate over it to release its items. */ |
|
node = LIST_FIRST(&tmp->tm_nodes); |
|
while (node != NULL) { |
|
struct tmpfs_node *next; |
|
|
|
if (node->tn_type == VDIR) { |
if (node->tn_type == VDIR) { |
struct tmpfs_dirent *de; |
tmpfs_dirent_t *de; |
|
|
|
/* Destroy any directory entries. */ |
de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir); |
de = TAILQ_FIRST(&node->tn_spec.tn_dir.tn_dir); |
while (de != NULL) { |
while (de != NULL) { |
struct tmpfs_dirent *nde; |
tmpfs_dirent_t *nde; |
|
|
nde = TAILQ_NEXT(de, td_entries); |
nde = TAILQ_NEXT(de, td_entries); |
tmpfs_free_dirent(tmp, de, false); |
tmpfs_free_dirent(tmp, de); |
|
node->tn_size -= sizeof(tmpfs_dirent_t); |
de = nde; |
de = nde; |
node->tn_size -= sizeof(struct tmpfs_dirent); |
|
} |
} |
} |
} |
|
/* Removes inode from the list. */ |
next = LIST_NEXT(node, tn_entries); |
|
tmpfs_free_node(tmp, node); |
tmpfs_free_node(tmp, node); |
node = next; |
|
} |
} |
|
|
/* Throw away the tmpfs_mount structure. */ |
/* Throw away the tmpfs_mount structure. */ |
Line 241 tmpfs_unmount(struct mount *mp, int mntf |
|
Line 249 tmpfs_unmount(struct mount *mp, int mntf |
|
return 0; |
return 0; |
} |
} |
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
static int |
static int |
tmpfs_root(struct mount *mp, struct vnode **vpp) |
tmpfs_root(struct mount *mp, vnode_t **vpp) |
{ |
{ |
|
tmpfs_node_t *node = VFS_TO_TMPFS(mp)->tm_root; |
|
|
return tmpfs_alloc_vp(mp, VFS_TO_TMPFS(mp)->tm_root, vpp); |
mutex_enter(&node->tn_vlock); |
|
return tmpfs_vnode_get(mp, node, vpp); |
} |
} |
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
static int |
static int |
tmpfs_vget(struct mount *mp, ino_t ino, |
tmpfs_vget(struct mount *mp, ino_t ino, vnode_t **vpp) |
struct vnode **vpp) |
|
{ |
{ |
|
|
printf("tmpfs_vget called; need for it unknown yet\n"); |
printf("tmpfs_vget called; need for it unknown yet\n"); |
return EOPNOTSUPP; |
return EOPNOTSUPP; |
} |
} |
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
static int |
static int |
tmpfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp) |
tmpfs_fhtovp(struct mount *mp, struct fid *fhp, vnode_t **vpp) |
{ |
{ |
bool found; |
tmpfs_mount_t *tmp = VFS_TO_TMPFS(mp); |
struct tmpfs_fid tfh; |
tmpfs_node_t *node; |
struct tmpfs_mount *tmp; |
tmpfs_fid_t tfh; |
struct tmpfs_node *node; |
|
|
|
tmp = VFS_TO_TMPFS(mp); |
|
|
|
if (fhp->fid_len != sizeof(struct tmpfs_fid)) |
|
return EINVAL; |
|
|
|
memcpy(&tfh, fhp, sizeof(struct tmpfs_fid)); |
|
|
|
if (tfh.tf_id >= tmp->tm_nodes_max) |
if (fhp->fid_len != sizeof(tmpfs_fid_t)) { |
return EINVAL; |
return EINVAL; |
|
} |
|
memcpy(&tfh, fhp, sizeof(tmpfs_fid_t)); |
|
|
found = false; |
|
mutex_enter(&tmp->tm_lock); |
mutex_enter(&tmp->tm_lock); |
LIST_FOREACH(node, &tmp->tm_nodes, tn_entries) { |
LIST_FOREACH(node, &tmp->tm_nodes, tn_entries) { |
if (node->tn_id == tfh.tf_id && |
if (node->tn_id != tfh.tf_id) { |
node->tn_gen == tfh.tf_gen) { |
continue; |
found = true; |
} |
break; |
if (TMPFS_NODE_GEN(node) != tfh.tf_gen) { |
|
continue; |
} |
} |
|
mutex_enter(&node->tn_vlock); |
|
break; |
} |
} |
mutex_exit(&tmp->tm_lock); |
mutex_exit(&tmp->tm_lock); |
|
|
/* XXXAD nothing to prevent 'node' from being removed. */ |
/* Will release the tn_vlock. */ |
return found ? tmpfs_alloc_vp(mp, node, vpp) : EINVAL; |
return node ? tmpfs_vnode_get(mp, node, vpp) : ESTALE; |
} |
} |
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
static int |
static int |
tmpfs_vptofh(struct vnode *vp, struct fid *fhp, size_t *fh_size) |
tmpfs_vptofh(vnode_t *vp, struct fid *fhp, size_t *fh_size) |
{ |
{ |
struct tmpfs_fid tfh; |
tmpfs_fid_t tfh; |
struct tmpfs_node *node; |
tmpfs_node_t *node; |
|
|
if (*fh_size < sizeof(struct tmpfs_fid)) { |
if (*fh_size < sizeof(tmpfs_fid_t)) { |
*fh_size = sizeof(struct tmpfs_fid); |
*fh_size = sizeof(tmpfs_fid_t); |
return E2BIG; |
return E2BIG; |
} |
} |
*fh_size = sizeof(struct tmpfs_fid); |
*fh_size = sizeof(tmpfs_fid_t); |
node = VP_TO_TMPFS_NODE(vp); |
node = VP_TO_TMPFS_NODE(vp); |
|
|
memset(&tfh, 0, sizeof(tfh)); |
memset(&tfh, 0, sizeof(tfh)); |
tfh.tf_len = sizeof(struct tmpfs_fid); |
tfh.tf_len = sizeof(tmpfs_fid_t); |
tfh.tf_gen = node->tn_gen; |
tfh.tf_gen = TMPFS_NODE_GEN(node); |
tfh.tf_id = node->tn_id; |
tfh.tf_id = node->tn_id; |
memcpy(fhp, &tfh, sizeof(tfh)); |
memcpy(fhp, &tfh, sizeof(tfh)); |
|
|
return 0; |
return 0; |
} |
} |
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
static int |
static int |
tmpfs_statvfs(struct mount *mp, struct statvfs *sbp) |
tmpfs_statvfs(struct mount *mp, struct statvfs *sbp) |
{ |
{ |
struct tmpfs_mount *tmp; |
tmpfs_mount_t *tmp; |
fsfilcnt_t freenodes; |
fsfilcnt_t freenodes; |
size_t avail; |
size_t avail; |
|
|
Line 340 tmpfs_statvfs(struct mount *mp, struct s |
|
Line 335 tmpfs_statvfs(struct mount *mp, struct s |
|
sbp->f_bresvd = 0; |
sbp->f_bresvd = 0; |
|
|
freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_cnt, |
freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_cnt, |
avail * PAGE_SIZE / sizeof(struct tmpfs_node)); |
avail * PAGE_SIZE / sizeof(tmpfs_node_t)); |
|
|
sbp->f_files = tmp->tm_nodes_cnt + freenodes; |
sbp->f_files = tmp->tm_nodes_cnt + freenodes; |
sbp->f_favail = sbp->f_ffree = freenodes; |
sbp->f_favail = sbp->f_ffree = freenodes; |
Line 352 tmpfs_statvfs(struct mount *mp, struct s |
|
Line 347 tmpfs_statvfs(struct mount *mp, struct s |
|
return 0; |
return 0; |
} |
} |
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
/* ARGSUSED0 */ |
|
static int |
static int |
tmpfs_sync(struct mount *mp, int waitfor, |
tmpfs_sync(struct mount *mp, int waitfor, kauth_cred_t uc) |
kauth_cred_t uc) |
|
{ |
{ |
|
|
return 0; |
return 0; |
} |
} |
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
static void |
|
tmpfs_init(void) |
|
{ |
|
|
|
} |
|
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
static void |
|
tmpfs_done(void) |
|
{ |
|
|
|
} |
|
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
static int |
static int |
tmpfs_snapshot(struct mount *mp, struct vnode *vp, |
tmpfs_snapshot(struct mount *mp, vnode_t *vp, struct timespec *ctime) |
struct timespec *ctime) |
|
{ |
{ |
|
|
return EOPNOTSUPP; |
return EOPNOTSUPP; |
} |
} |
|
|
/* --------------------------------------------------------------------- */ |
|
|
|
/* |
/* |
* tmpfs vfs operations. |
* tmpfs vfs operations. |
*/ |
*/ |