root/sys/fs/tarfs/tarfs.h
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2013 Juniper Networks, Inc.
 * Copyright (c) 2022-2023 Klara, Inc.
 *
 * 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 AND CONTRIBUTORS ``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.
 */

#ifndef _FS_TARFS_TARFS_H_
#define _FS_TARFS_TARFS_H_

#ifndef _KERNEL
#error Should only be included by kernel
#endif

MALLOC_DECLARE(M_TARFSMNT);
MALLOC_DECLARE(M_TARFSNODE);
MALLOC_DECLARE(M_TARFSNAME);

#ifdef SYSCTL_DECL
SYSCTL_DECL(_vfs_tarfs);
#endif

struct componentname;
struct mount;
struct vnode;

/*
 * Internal representation of a tarfs file system node.
 */
struct tarfs_node {
        TAILQ_ENTRY(tarfs_node) entries;
        TAILQ_ENTRY(tarfs_node) dirents;

        struct mtx               lock;

        struct vnode            *vnode;
        struct tarfs_mount      *tmp;
        __enum_uint8(vtype)      type;
        ino_t                    ino;
        off_t                    offset;
        size_t                   size;
        size_t                   physize;
        char                    *name;
        size_t                   namelen;

        /* Node attributes */
        uid_t                    uid;
        gid_t                    gid;
        mode_t                   mode;
        unsigned int             flags;
        nlink_t                  nlink;
        struct timespec          atime;
        struct timespec          mtime;
        struct timespec          ctime;
        struct timespec          birthtime;
        uint32_t                 gen;

        /* Block map */
        size_t                   nblk;
        struct tarfs_blk        *blk;

        struct tarfs_node       *parent;
        union {
                /* VDIR */
                struct {
                        TAILQ_HEAD(, tarfs_node) dirhead;
                        off_t                    lastcookie;
                        struct tarfs_node       *lastnode;
                } dir;

                /* VLNK */
                struct {
                        char                    *name;
                        size_t                   namelen;
                } link;

                /* VBLK or VCHR */
                dev_t                    rdev;

                /* VREG */
                struct tarfs_node       *other;
        };
};

/*
 * Entry in sparse file block map.
 */
struct tarfs_blk {
        off_t    i;             /* input (physical) offset */
        off_t    o;             /* output (logical) offset */
        size_t   l;             /* length */
};

/*
 * Decompression buffer.
 */
#define TARFS_ZBUF_SIZE 1048576
struct tarfs_zbuf {
        u_char           buf[TARFS_ZBUF_SIZE];
        size_t           off; /* offset of contents */
        size_t           len; /* length of contents */
};

/*
 * Internal representation of a tarfs mount point.
 */
struct tarfs_mount {
        TAILQ_HEAD(, tarfs_node) allnodes;
        struct mtx               allnode_lock;

        struct tarfs_node       *root;
        struct vnode            *vp;
        struct mount            *vfs;
        ino_t                    ino;
        struct unrhdr           *ino_unr;
        size_t                   iosize;
        size_t                   nblocks;
        size_t                   nfiles;
        time_t                   mtime; /* default mtime for directories */

        struct tarfs_zio        *zio;
        struct vnode            *znode;
};

struct tarfs_zio {
        struct tarfs_mount      *tmp;

        /* decompression state */
#ifdef ZSTDIO
        struct tarfs_zstd       *zstd; /* decompression state (zstd) */
#endif
        off_t                    ipos; /* current input position */
        off_t                    opos; /* current output position */

        /* index of compression frames */
        unsigned int             curidx; /* current index position*/
        unsigned int             nidx; /* number of index entries */
        unsigned int             szidx; /* index capacity */
        struct tarfs_idx { off_t i, o; } *idx;
};

struct tarfs_fid {
        u_short          len;   /* length of data in bytes */
        uint32_t         gen;
        ino_t            ino;
};

#define TARFS_NODE_LOCK(tnp) \
        mtx_lock(&(tnp)->lock)
#define TARFS_NODE_UNLOCK(tnp) \
        mtx_unlock(&(tnp)->lock)
#define TARFS_ALLNODES_LOCK(tnp) \
        mtx_lock(&(tmp)->allnode_lock)
#define TARFS_ALLNODES_UNLOCK(tnp) \
        mtx_unlock(&(tmp)->allnode_lock)

/*
 * Data and metadata within tar files are aligned on 512-byte boundaries,
 * to match the block size of the magnetic tapes they were originally
 * intended for.
 */
#define TARFS_BSHIFT            9
#define TARFS_BLOCKSIZE         (size_t)(1U << TARFS_BSHIFT)
#define TARFS_BLKOFF(l)         ((l) % TARFS_BLOCKSIZE)
#define TARFS_BLKNUM(l)         ((l) >> TARFS_BSHIFT)
#define TARFS_SZ2BLKS(sz)       (((sz) + TARFS_BLOCKSIZE - 1) / TARFS_BLOCKSIZE)

/*
 * Our preferred I/O size.
 */
extern unsigned int tarfs_ioshift;
#define TARFS_IOSHIFT_MIN       TARFS_BSHIFT
#define TARFS_IOSHIFT_DEFAULT   PAGE_SHIFT
#define TARFS_IOSHIFT_MAX       PAGE_SHIFT

#define TARFS_ROOTINO           ((ino_t)3)
#define TARFS_ZIOINO            ((ino_t)4)
#define TARFS_MININO            ((ino_t)65535)

#define TARFS_COOKIE_DOT        0
#define TARFS_COOKIE_DOTDOT     1
#define TARFS_COOKIE_EOF        OFF_MAX

#define TARFS_ZIO_NAME          ".tar"
#define TARFS_ZIO_NAMELEN       (sizeof(TARFS_ZIO_NAME) - 1)

extern struct vop_vector tarfs_vnodeops;

static inline
struct tarfs_mount *
MP_TO_TARFS_MOUNT(struct mount *mp)
{

        MPASS(mp != NULL && mp->mnt_data != NULL);
        return (mp->mnt_data);
}

static inline
struct tarfs_node *
VP_TO_TARFS_NODE(struct vnode *vp)
{

        MPASS(vp != NULL && vp->v_data != NULL);
        return (vp->v_data);
}

int     tarfs_alloc_node(struct tarfs_mount *tmp, const char *name,
            size_t namelen, __enum_uint8(vtype) type, off_t off, size_t sz,
            time_t mtime, uid_t uid, gid_t gid, mode_t mode,
            unsigned int flags, const char *linkname, dev_t rdev,
            struct tarfs_node *parent, struct tarfs_node **node);
int     tarfs_load_blockmap(struct tarfs_node *tnp, size_t realsize);
void    tarfs_free_node(struct tarfs_node *tnp);
struct tarfs_node *
        tarfs_lookup_dir(struct tarfs_node *tnp, off_t cookie);
struct tarfs_node *
        tarfs_lookup_node(struct tarfs_node *tnp, struct tarfs_node *f,
            struct componentname *cnp);
int     tarfs_read_file(struct tarfs_node *tnp, size_t len, struct uio *uiop);

int     tarfs_io_init(struct tarfs_mount *tmp);
int     tarfs_io_fini(struct tarfs_mount *tmp);
int     tarfs_io_read(struct tarfs_mount *tmp, bool raw,
    struct uio *uiop);
ssize_t tarfs_io_read_buf(struct tarfs_mount *tmp, bool raw,
    void *buf, off_t off, size_t len);
unsigned int
        tarfs_strtofflags(const char *str, char **end);

#endif  /* _FS_TARFS_TARFS_H_ */