aboutsummaryrefslogtreecommitdiff
path: root/sys/fs/tmpfs
diff options
context:
space:
mode:
Diffstat (limited to 'sys/fs/tmpfs')
-rw-r--r--sys/fs/tmpfs/tmpfs.h17
-rw-r--r--sys/fs/tmpfs/tmpfs_subr.c106
-rw-r--r--sys/fs/tmpfs/tmpfs_vfsops.c18
-rw-r--r--sys/fs/tmpfs/tmpfs_vnops.c136
4 files changed, 133 insertions, 144 deletions
diff --git a/sys/fs/tmpfs/tmpfs.h b/sys/fs/tmpfs/tmpfs.h
index c28f3a02a7bf..52307cc7c7b2 100644
--- a/sys/fs/tmpfs/tmpfs.h
+++ b/sys/fs/tmpfs/tmpfs.h
@@ -292,6 +292,15 @@ struct tmpfs_node {
*/
off_t tn_readdir_lastn;
struct tmpfs_dirent * tn_readdir_lastp;
+
+ /*
+ * Total size of whiteout directory entries. This
+ * must be a multiple of sizeof(struct tmpfs_dirent)
+ * and is used to determine whether a directory is
+ * empty (excluding whiteout entries) during rename/
+ * rmdir operations.
+ */
+ off_t tn_wht_size; /* (v) */
} tn_dir;
/* Valid when tn_type == VLNK. */
@@ -439,11 +448,10 @@ struct tmpfs_mount {
* NFS code.
*/
struct tmpfs_fid_data {
+ unsigned short tfd_len;
ino_t tfd_id;
unsigned long tfd_gen;
-};
-_Static_assert(sizeof(struct tmpfs_fid_data) <= MAXFIDSZ,
- "(struct tmpfs_fid_data) is larger than (struct fid).fid_data");
+} __packed;
struct tmpfs_dir_cursor {
struct tmpfs_dirent *tdc_current;
@@ -484,6 +492,7 @@ int tmpfs_dir_getdents(struct tmpfs_mount *, struct tmpfs_node *,
struct uio *, int, uint64_t *, int *);
int tmpfs_dir_whiteout_add(struct vnode *, struct componentname *);
void tmpfs_dir_whiteout_remove(struct vnode *, struct componentname *);
+void tmpfs_dir_clear_whiteouts(struct vnode *);
int tmpfs_reg_resize(struct vnode *, off_t, boolean_t);
int tmpfs_reg_punch_hole(struct vnode *vp, off_t *, off_t *);
int tmpfs_chflags(struct vnode *, u_long, struct ucred *, struct thread *);
@@ -533,6 +542,8 @@ tmpfs_update(struct vnode *vp)
#define TMPFS_VALIDATE_DIR(node) do { \
MPASS((node)->tn_type == VDIR); \
MPASS((node)->tn_size % sizeof(struct tmpfs_dirent) == 0); \
+ MPASS((node)->tn_dir.tn_wht_size % sizeof(struct tmpfs_dirent) == 0); \
+ MPASS((node)->tn_dir.tn_wht_size <= (node)->tn_size); \
} while (0)
/*
diff --git a/sys/fs/tmpfs/tmpfs_subr.c b/sys/fs/tmpfs/tmpfs_subr.c
index 9bdcc4575511..1237f6b92cdb 100644
--- a/sys/fs/tmpfs/tmpfs_subr.c
+++ b/sys/fs/tmpfs/tmpfs_subr.c
@@ -120,7 +120,7 @@ tmpfs_pager_writecount_recalc(vm_object_t object, vm_offset_t old,
/*
* Forced unmount?
*/
- if (vp == NULL) {
+ if (vp == NULL || vp->v_object == NULL) {
KASSERT((object->flags & OBJ_TMPFS_VREF) == 0,
("object %p with OBJ_TMPFS_VREF but without vnode",
object));
@@ -183,6 +183,9 @@ tmpfs_pager_release_writecount(vm_object_t object, vm_offset_t start,
KASSERT((object->flags & OBJ_ANON) == 0,
("%s: object %p with OBJ_ANON", __func__, object));
old = object->un_pager.swp.writemappings;
+ KASSERT(old >= (vm_ooffset_t)end - start,
+ ("tmpfs obj %p writecount %jx dec %jx", object, (uintmax_t)old,
+ (uintmax_t)((vm_ooffset_t)end - start)));
object->un_pager.swp.writemappings -= (vm_ooffset_t)end - start;
new = object->un_pager.swp.writemappings;
tmpfs_pager_writecount_recalc(object, old, new);
@@ -346,7 +349,7 @@ tmpfs_node_init(void *mem, int size, int flags)
node = mem;
node->tn_id = 0;
- mtx_init(&node->tn_interlock, "tmpfsni", NULL, MTX_DEF);
+ mtx_init(&node->tn_interlock, "tmpfsni", NULL, MTX_DEF | MTX_NEW);
node->tn_gen = arc4random();
return (0);
}
@@ -425,7 +428,7 @@ sysctl_mem_percent(SYSCTL_HANDLER_ARGS)
if ((unsigned) percent > 100)
return (EINVAL);
- *(long *)arg1 = percent;
+ *(int *)arg1 = percent;
tmpfs_set_reserve_from_percent();
return (0);
}
@@ -440,7 +443,7 @@ tmpfs_set_reserve_from_percent(void)
}
SYSCTL_PROC(_vfs_tmpfs, OID_AUTO, memory_percent,
- CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RW, &tmpfs_mem_percent, 0,
+ CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RWTUN, &tmpfs_mem_percent, 0,
sysctl_mem_percent, "I",
"Percent of available memory that can be used if no size limit");
@@ -490,50 +493,11 @@ static int
tmpfs_partial_page_invalidate(vm_object_t object, vm_pindex_t idx, int base,
int end, boolean_t ignerr)
{
- vm_page_t m;
- int rv, error;
-
- VM_OBJECT_ASSERT_WLOCKED(object);
- KASSERT(base >= 0, ("%s: base %d", __func__, base));
- KASSERT(end - base <= PAGE_SIZE, ("%s: base %d end %d", __func__, base,
- end));
- error = 0;
-
-retry:
- m = vm_page_grab(object, idx, VM_ALLOC_NOCREAT);
- if (m != NULL) {
- MPASS(vm_page_all_valid(m));
- } else if (vm_pager_has_page(object, idx, NULL, NULL)) {
- m = vm_page_alloc(object, idx, VM_ALLOC_NORMAL |
- VM_ALLOC_WAITFAIL);
- if (m == NULL)
- goto retry;
- vm_object_pip_add(object, 1);
- VM_OBJECT_WUNLOCK(object);
- rv = vm_pager_get_pages(object, &m, 1, NULL, NULL);
- VM_OBJECT_WLOCK(object);
- vm_object_pip_wakeup(object);
- if (rv == VM_PAGER_OK) {
- /*
- * Since the page was not resident, and therefore not
- * recently accessed, immediately enqueue it for
- * asynchronous laundering. The current operation is
- * not regarded as an access.
- */
- vm_page_launder(m);
- } else {
- vm_page_free(m);
- m = NULL;
- if (!ignerr)
- error = EIO;
- }
- }
- if (m != NULL) {
- pmap_zero_page_area(m, base, end - base);
- vm_page_set_dirty(m);
- vm_page_xunbusy(m);
- }
+ int error;
+ error = vm_page_grab_zero_partial(object, idx, base, end);
+ if (ignerr)
+ error = 0;
return (error);
}
@@ -643,6 +607,7 @@ tmpfs_alloc_node(struct mount *mp, struct tmpfs_mount *tmp, __enum_uint8(vtype)
nnode->tn_dir.tn_parent = (parent == NULL) ? nnode : parent;
nnode->tn_dir.tn_readdir_lastn = 0;
nnode->tn_dir.tn_readdir_lastp = NULL;
+ nnode->tn_dir.tn_wht_size = 0;
nnode->tn_links++;
TMPFS_NODE_LOCK(nnode->tn_dir.tn_parent);
nnode->tn_dir.tn_parent->tn_links++;
@@ -954,6 +919,8 @@ tmpfs_destroy_vobject(struct vnode *vp, vm_object_t obj)
VM_OBJECT_WLOCK(obj);
VI_LOCK(vp);
+ vp->v_object = NULL;
+
/*
* May be going through forced unmount.
*/
@@ -1094,15 +1061,19 @@ loop:
KASSERT((object->flags & OBJ_TMPFS_VREF) == 0,
("%s: object %p with OBJ_TMPFS_VREF but without vnode",
__func__, object));
- KASSERT(object->un_pager.swp.writemappings == 0,
- ("%s: object %p has writemappings",
- __func__, object));
VI_LOCK(vp);
KASSERT(vp->v_object == NULL, ("Not NULL v_object in tmpfs"));
vp->v_object = object;
vn_irflag_set_locked(vp, (tm->tm_pgread ? VIRF_PGREAD : 0) |
VIRF_TEXT_REF);
VI_UNLOCK(vp);
+ VNASSERT((object->flags & OBJ_TMPFS_VREF) == 0, vp,
+ ("leaked OBJ_TMPFS_VREF"));
+ if (object->un_pager.swp.writemappings > 0) {
+ vrefact(vp);
+ vlazy(vp);
+ vm_object_set_flag(object, OBJ_TMPFS_VREF);
+ }
VM_OBJECT_WUNLOCK(object);
break;
case VDIR:
@@ -1822,13 +1793,16 @@ int
tmpfs_dir_whiteout_add(struct vnode *dvp, struct componentname *cnp)
{
struct tmpfs_dirent *de;
+ struct tmpfs_node *dnode;
int error;
error = tmpfs_alloc_dirent(VFS_TO_TMPFS(dvp->v_mount), NULL,
cnp->cn_nameptr, cnp->cn_namelen, &de);
if (error != 0)
return (error);
+ dnode = VP_TO_TMPFS_DIR(dvp);
tmpfs_dir_attach(dvp, de);
+ dnode->tn_dir.tn_wht_size += sizeof(*de);
return (0);
}
@@ -1836,14 +1810,44 @@ void
tmpfs_dir_whiteout_remove(struct vnode *dvp, struct componentname *cnp)
{
struct tmpfs_dirent *de;
+ struct tmpfs_node *dnode;
- de = tmpfs_dir_lookup(VP_TO_TMPFS_DIR(dvp), NULL, cnp);
+ dnode = VP_TO_TMPFS_DIR(dvp);
+ de = tmpfs_dir_lookup(dnode, NULL, cnp);
MPASS(de != NULL && de->td_node == NULL);
+ MPASS(dnode->tn_dir.tn_wht_size >= sizeof(*de));
+ dnode->tn_dir.tn_wht_size -= sizeof(*de);
tmpfs_dir_detach(dvp, de);
tmpfs_free_dirent(VFS_TO_TMPFS(dvp->v_mount), de);
}
/*
+ * Frees any dirents still associated with the directory represented
+ * by dvp in preparation for the removal of the directory. This is
+ * required when removing a directory which contains only whiteout
+ * entries.
+ */
+void
+tmpfs_dir_clear_whiteouts(struct vnode *dvp)
+{
+ struct tmpfs_dir_cursor dc;
+ struct tmpfs_dirent *de;
+ struct tmpfs_node *dnode;
+
+ dnode = VP_TO_TMPFS_DIR(dvp);
+
+ while ((de = tmpfs_dir_first(dnode, &dc)) != NULL) {
+ KASSERT(de->td_node == NULL, ("%s: non-whiteout dirent %p",
+ __func__, de));
+ dnode->tn_dir.tn_wht_size -= sizeof(*de);
+ tmpfs_dir_detach(dvp, de);
+ tmpfs_free_dirent(VFS_TO_TMPFS(dvp->v_mount), de);
+ }
+ MPASS(dnode->tn_size == 0);
+ MPASS(dnode->tn_dir.tn_wht_size == 0);
+}
+
+/*
* Resizes the aobj associated with the regular file pointed to by 'vp' to the
* size 'newsize'. 'vp' must point to a vnode that represents a regular file.
* 'newsize' must be positive.
diff --git a/sys/fs/tmpfs/tmpfs_vfsops.c b/sys/fs/tmpfs/tmpfs_vfsops.c
index 32eb9c958df1..431893b77bb9 100644
--- a/sys/fs/tmpfs/tmpfs_vfsops.c
+++ b/sys/fs/tmpfs/tmpfs_vfsops.c
@@ -208,7 +208,7 @@ again:
continue;
}
vm = vmspace_acquire_ref(p);
- _PHOLD_LITE(p);
+ _PHOLD(p);
PROC_UNLOCK(p);
if (vm == NULL) {
PRELE(p);
@@ -585,29 +585,25 @@ static int
tmpfs_fhtovp(struct mount *mp, struct fid *fhp, int flags,
struct vnode **vpp)
{
- struct tmpfs_fid_data tfd;
+ struct tmpfs_fid_data *tfd;
struct tmpfs_mount *tmp;
struct tmpfs_node *node;
int error;
- if (fhp->fid_len != sizeof(tfd))
+ if (fhp->fid_len != sizeof(*tfd))
return (EINVAL);
- /*
- * Copy from fid_data onto the stack to avoid unaligned pointer use.
- * See the comment in sys/mount.h on struct fid for details.
- */
- memcpy(&tfd, fhp->fid_data, fhp->fid_len);
+ tfd = (struct tmpfs_fid_data *)fhp;
tmp = VFS_TO_TMPFS(mp);
- if (tfd.tfd_id >= tmp->tm_nodes_max)
+ if (tfd->tfd_id >= tmp->tm_nodes_max)
return (EINVAL);
TMPFS_LOCK(tmp);
LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) {
- if (node->tn_id == tfd.tfd_id &&
- node->tn_gen == tfd.tfd_gen) {
+ if (node->tn_id == tfd->tfd_id &&
+ node->tn_gen == tfd->tfd_gen) {
tmpfs_ref_node(node);
break;
}
diff --git a/sys/fs/tmpfs/tmpfs_vnops.c b/sys/fs/tmpfs/tmpfs_vnops.c
index 718cfef6bfa3..9d2a587b177a 100644
--- a/sys/fs/tmpfs/tmpfs_vnops.c
+++ b/sys/fs/tmpfs/tmpfs_vnops.c
@@ -476,6 +476,7 @@ tmpfs_stat(struct vop_stat_args *v)
sb->st_blksize = PAGE_SIZE;
sb->st_flags = node->tn_flags;
sb->st_gen = node->tn_gen;
+ sb->st_filerev = 0;
if (vp->v_type == VREG) {
#ifdef __ILP32__
vm_object_t obj = node->tn_reg.tn_aobj;
@@ -1078,7 +1079,9 @@ tmpfs_rename(struct vop_rename_args *v)
}
if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
- if (tnode->tn_size > 0) {
+ if (tnode->tn_size != 0 &&
+ ((tcnp->cn_flags & IGNOREWHITEOUT) == 0 ||
+ tnode->tn_size > tnode->tn_dir.tn_wht_size)) {
error = ENOTEMPTY;
goto out_locked;
}
@@ -1239,6 +1242,16 @@ tmpfs_rename(struct vop_rename_args *v)
tde = tmpfs_dir_lookup(tdnode, tnode, tcnp);
tmpfs_dir_detach(tdvp, tde);
+ /*
+ * If we are overwriting a directory, per the ENOTEMPTY check
+ * above it must either be empty or contain only whiteout
+ * entries. In the latter case (which can only happen if
+ * IGNOREWHITEOUT was passed in tcnp->cn_flags), clear the
+ * whiteout entries to avoid leaking memory.
+ */
+ if (tnode->tn_type == VDIR && tnode->tn_size > 0)
+ tmpfs_dir_clear_whiteouts(tvp);
+
/* Update node's ctime because of possible hardlinks. */
tnode->tn_status |= TMPFS_NODE_CHANGED;
tmpfs_update(tvp);
@@ -1309,6 +1322,7 @@ tmpfs_rmdir(struct vop_rmdir_args *v)
{
struct vnode *dvp = v->a_dvp;
struct vnode *vp = v->a_vp;
+ struct componentname *cnp = v->a_cnp;
int error;
struct tmpfs_dirent *de;
@@ -1320,13 +1334,18 @@ tmpfs_rmdir(struct vop_rmdir_args *v)
dnode = VP_TO_TMPFS_DIR(dvp);
node = VP_TO_TMPFS_DIR(vp);
- /* Directories with more than two entries ('.' and '..') cannot be
- * removed. */
- if (node->tn_size > 0) {
- error = ENOTEMPTY;
- goto out;
- }
+ /*
+ * Directories with more than two non-whiteout entries ('.' and '..')
+ * cannot be removed.
+ */
+ if (node->tn_size != 0 &&
+ ((cnp->cn_flags & IGNOREWHITEOUT) == 0 ||
+ node->tn_size > node->tn_dir.tn_wht_size)) {
+ error = ENOTEMPTY;
+ goto out;
+ }
+ /* Check flags to see if we are allowed to remove the directory. */
if ((dnode->tn_flags & APPEND)
|| (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
error = EPERM;
@@ -1334,27 +1353,31 @@ tmpfs_rmdir(struct vop_rmdir_args *v)
}
/* This invariant holds only if we are not trying to remove "..".
- * We checked for that above so this is safe now. */
+ * We checked for that above so this is safe now. */
MPASS(node->tn_dir.tn_parent == dnode);
/* Get the directory entry associated with node (vp). This was
* filled by tmpfs_lookup while looking up the entry. */
- de = tmpfs_dir_lookup(dnode, node, v->a_cnp);
+ de = tmpfs_dir_lookup(dnode, node, cnp);
MPASS(TMPFS_DIRENT_MATCHES(de,
- v->a_cnp->cn_nameptr,
- v->a_cnp->cn_namelen));
-
- /* Check flags to see if we are allowed to remove the directory. */
- if ((dnode->tn_flags & APPEND) != 0 ||
- (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) != 0) {
- error = EPERM;
- goto out;
- }
+ cnp->cn_nameptr,
+ cnp->cn_namelen));
/* Detach the directory entry from the directory (dnode). */
tmpfs_dir_detach(dvp, de);
- if (v->a_cnp->cn_flags & DOWHITEOUT)
- tmpfs_dir_whiteout_add(dvp, v->a_cnp);
+
+ /*
+ * If we are removing a directory, per the ENOTEMPTY check above it
+ * must either be empty or contain only whiteout entries. In the
+ * latter case (which can only happen if IGNOREWHITEOUT was passed
+ * in cnp->cn_flags), clear the whiteout entries to avoid leaking
+ * memory.
+ */
+ if (node->tn_size > 0)
+ tmpfs_dir_clear_whiteouts(vp);
+
+ if (cnp->cn_flags & DOWHITEOUT)
+ tmpfs_dir_whiteout_add(dvp, cnp);
/* No vnode should be allocated for this entry from this point */
TMPFS_NODE_LOCK(node);
@@ -1668,6 +1691,10 @@ tmpfs_pathconf(struct vop_pathconf_args *v)
*retval = PAGE_SIZE;
break;
+ case _PC_HAS_HIDDENSYSTEM:
+ *retval = 1;
+ break;
+
default:
error = vop_stdpathconf(v);
}
@@ -1684,21 +1711,15 @@ vop_vptofh {
};
*/
{
- struct tmpfs_fid_data tfd;
+ struct tmpfs_fid_data *const tfd = (struct tmpfs_fid_data *)ap->a_fhp;
struct tmpfs_node *node;
- struct fid *fhp;
+ _Static_assert(sizeof(struct tmpfs_fid_data) <= sizeof(struct fid),
+ "struct tmpfs_fid_data cannot be larger than struct fid");
node = VP_TO_TMPFS_NODE(ap->a_vp);
- fhp = ap->a_fhp;
- fhp->fid_len = sizeof(tfd);
-
- /*
- * Copy into fid_data from the stack to avoid unaligned pointer use.
- * See the comment in sys/mount.h on struct fid for details.
- */
- tfd.tfd_id = node->tn_id;
- tfd.tfd_gen = node->tn_gen;
- memcpy(fhp->fid_data, &tfd, fhp->fid_len);
+ tfd->tfd_len = sizeof(*tfd);
+ tfd->tfd_gen = node->tn_gen;
+ tfd->tfd_id = node->tn_id;
return (0);
}
@@ -2070,31 +2091,10 @@ tmpfs_setextattr(struct vop_setextattr_args *ap)
static off_t
tmpfs_seek_data_locked(vm_object_t obj, off_t noff)
{
- vm_page_t m;
- vm_pindex_t p, p_m, p_swp;
-
- p = OFF_TO_IDX(noff);
- m = vm_page_find_least(obj, p);
-
- /*
- * Microoptimize the most common case for SEEK_DATA, where
- * there is no hole and the page is resident.
- */
- if (m != NULL && vm_page_any_valid(m) && m->pindex == p)
- return (noff);
-
- p_swp = swap_pager_find_least(obj, p);
- if (p_swp == p)
- return (noff);
-
- p_m = m == NULL ? obj->size : m->pindex;
- return (IDX_TO_OFF(MIN(p_m, p_swp)));
-}
+ vm_pindex_t p;
-static off_t
-tmpfs_seek_next(off_t noff)
-{
- return (noff + PAGE_SIZE - (noff & PAGE_MASK));
+ p = swap_pager_seek_data(obj, OFF_TO_IDX(noff));
+ return (p == OFF_TO_IDX(noff) ? noff : IDX_TO_OFF(p));
}
static int
@@ -2111,30 +2111,8 @@ tmpfs_seek_clamp(struct tmpfs_node *tn, off_t *noff, bool seekdata)
static off_t
tmpfs_seek_hole_locked(vm_object_t obj, off_t noff)
{
- vm_page_t m;
- vm_pindex_t p, p_swp;
-
- for (;; noff = tmpfs_seek_next(noff)) {
- /*
- * Walk over the largest sequential run of the valid pages.
- */
- for (m = vm_page_lookup(obj, OFF_TO_IDX(noff));
- m != NULL && vm_page_any_valid(m);
- m = vm_page_next(m), noff = tmpfs_seek_next(noff))
- ;
- /*
- * Found a hole in the object's page queue. Check if
- * there is a hole in the swap at the same place.
- */
- p = OFF_TO_IDX(noff);
- p_swp = swap_pager_find_least(obj, p);
- if (p_swp != p) {
- noff = IDX_TO_OFF(p);
- break;
- }
- }
- return (noff);
+ return (IDX_TO_OFF(swap_pager_seek_hole(obj, OFF_TO_IDX(noff))));
}
static int