root/usr/src/cmd/fs.d/udfs/fstyp/fstyp.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * libfstyp module for udfs
 */
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <locale.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/file.h>
#include <libnvpair.h>
#include <libfstyp_module.h>
#include <sys/fs/udf_volume.h>
#include "ud_lib.h"


typedef struct fstyp_udfs {
        int             fd;
        ud_handle_t     udh;
        nvlist_t        *attr;
} fstyp_udfs_t;

static int      is_udfs(fstyp_udfs_t *h);
static int      print_vds(fstyp_udfs_t *h, struct vds *,
                FILE *fout, FILE *ferr);
static int      get_attr(fstyp_udfs_t *h);

int     fstyp_mod_init(int fd, off_t offset, fstyp_mod_handle_t *handle);
void    fstyp_mod_fini(fstyp_mod_handle_t handle);
int     fstyp_mod_ident(fstyp_mod_handle_t handle);
int     fstyp_mod_get_attr(fstyp_mod_handle_t handle, nvlist_t **attrp);
int     fstyp_mod_dump(fstyp_mod_handle_t handle, FILE *fout, FILE *ferr);


int
fstyp_mod_init(int fd, off_t offset, fstyp_mod_handle_t *handle)
{
        fstyp_udfs_t *h = (fstyp_udfs_t *)handle;

        if (offset != 0) {
                return (FSTYP_ERR_OFFSET);
        }

        if ((h = calloc(1, sizeof (fstyp_udfs_t))) == NULL) {
                return (FSTYP_ERR_NOMEM);
        }
        h->fd = fd;

        if (ud_init(h->fd, &h->udh) != 0) {
                free(h);
                return (FSTYP_ERR_NOMEM);
        }

        *handle = (fstyp_mod_handle_t)h;
        return (0);
}

void
fstyp_mod_fini(fstyp_mod_handle_t handle)
{
        fstyp_udfs_t *h = (fstyp_udfs_t *)handle;

        if (h->attr == NULL) {
                nvlist_free(h->attr);
                h->attr = NULL;
        }
        ud_fini(h->udh);
        free(h);
}

int
fstyp_mod_ident(fstyp_mod_handle_t handle)
{
        fstyp_udfs_t *h = (fstyp_udfs_t *)handle;

        return (is_udfs(h));
}

int
fstyp_mod_get_attr(fstyp_mod_handle_t handle, nvlist_t **attrp)
{
        fstyp_udfs_t *h = (fstyp_udfs_t *)handle;
        int error;

        if (h->attr == NULL) {
                if (nvlist_alloc(&h->attr, NV_UNIQUE_NAME_TYPE, 0)) {
                        return (FSTYP_ERR_NOMEM);
                }
                if ((error = get_attr(h)) != 0) {
                        nvlist_free(h->attr);
                        h->attr = NULL;
                        return (error);
                }
        }

        *attrp = h->attr;
        return (0);
}

int
fstyp_mod_dump(fstyp_mod_handle_t handle, FILE *fout, FILE *ferr)
{
        fstyp_udfs_t *h = (fstyp_udfs_t *)handle;
        struct udf *udfs = &h->udh->udfs;
        int ret;

        (void) fprintf(fout,
                "Standard Identifier %5s\n", udfs->ecma_id);

        if (udfs->flags & VALID_MVDS) {
                ret = print_vds(h, &udfs->mvds, fout, ferr);
        } else {
                ret = print_vds(h, &udfs->rvds, fout, ferr);
        }

        return (ret);
}


/*
 * Assumption is that we will confirm to level-1
 */
int
is_udfs(fstyp_udfs_t *h)
{
        struct udf *udfs = &h->udh->udfs;
        int32_t ret;

        if ((ret = ud_fill_udfs_info(h->udh)) != 0) {
                return (ret);
        }

        if ((udfs->flags & VALID_UDFS) == 0) {
                return (FSTYP_ERR_NO_MATCH);
        }

        return (0);
}

/*
 * For now, only return generic attributes.
 * Will open an RFE to add native attributes.
 */
static int
get_attr(fstyp_udfs_t *h)
{
        struct udf *udfs = &h->udh->udfs;
        struct vds *v;
        struct pri_vol_desc *pvd;
        uint32_t len;
        uint64_t off;
        uint8_t *buf;
        int8_t str[64];
        int ret = 0;

        v = (udfs->flags & VALID_MVDS) ? &udfs->mvds : &udfs->rvds;

        /* allocate buffer */
        len = udfs->lbsize;
        if (v->pvd_len > len) {
                len = v->pvd_len;
        }
        if ((buf = (uint8_t *)malloc(len)) == NULL) {
                return (FSTYP_ERR_NOMEM);
        }

        (void) nvlist_add_boolean_value(h->attr, "gen_clean", B_TRUE);

        /* Primary Volume Descriptor */
        if (v->pvd_len != 0) {
                off = v->pvd_loc * udfs->lbsize;
                if (ud_read_dev(h->udh, off, buf, v->pvd_len) != 0) {
                        ret = FSTYP_ERR_IO;
                        goto out;
                }
                /* LINTED */
                pvd = (struct pri_vol_desc *)(uint32_t *)buf;

                ud_convert2local(pvd->pvd_vol_id, str, 32);
                str[32] = '\0';
                (void) nvlist_add_string(h->attr, "gen_volume_label", str);
        }

        ret = 0;

out:
        free(buf);
        return (ret);
}

/* ARGSUSED */
int
print_vds(fstyp_udfs_t *h, struct vds *v, FILE *fout, FILE *ferr)
{
        struct udf *udfs = &h->udh->udfs;
        int32_t i;
        uint32_t len;
        uint64_t off;
        uint8_t *buf;
        int     ret = 0;

        /*
         * All descriptors are 512 bytes
         * except lvd, usd and lvid
         * findout the largest and allocate space
         */
        len = udfs->lbsize;
        if (v->lvd_len > len) {
                len = v->lvd_len;
        }
        if (v->usd_len > len) {
                len = v->usd_len;
        }
        if (udfs->lvid_len > len) {
                len = udfs->lvid_len;
        }

        if ((buf = (uint8_t *)malloc(len)) == NULL) {
                return (FSTYP_ERR_NOMEM);
        }

        /*
         * Anchor Volume Descriptor
         */
        if (udfs->avdp_len != 0) {
                off = udfs->avdp_loc * udfs->lbsize;
                if (ud_read_dev(h->udh, off, buf, udfs->avdp_len) != 0) {
                        ret = FSTYP_ERR_IO;
                        goto out;
                }

                /* LINTED */
                print_avd(fout, (struct anch_vol_desc_ptr *)buf);
        }

        /*
         * Primary Volume Descriptor
         */
        if (v->pvd_len != 0) {
                off = v->pvd_loc * udfs->lbsize;
                if (ud_read_dev(h->udh, off, buf, v->pvd_len) != 0) {
                        ret = FSTYP_ERR_IO;
                        goto out;
                }

                /* LINTED */
                print_pvd(fout, (struct pri_vol_desc *)buf);
        }

        /*
         * Implementation Use descriptor
         */
        if (v->iud_len != 0) {
                off = v->iud_loc * udfs->lbsize;
                if (ud_read_dev(h->udh, off, buf, v->iud_len) != 0) {
                        ret = FSTYP_ERR_IO;
                        goto out;
                }

                /* LINTED */
                print_iuvd(fout, (struct iuvd_desc *)buf);
        }

        /*
         * Paritions
         */
        for (i = 0; i < h->udh->n_parts; i++) {
                if (v->part_len[i] != 0) {
                        off = v->part_loc[i] * udfs->lbsize;
                        if (ud_read_dev(h->udh, off, buf,
                            v->part_len[i]) != 0) {
                                ret = FSTYP_ERR_IO;
                                goto out;
                        }

                        /* LINTED */
                        print_part(fout, (struct part_desc *)buf);
                }
        }

        /*
         * Logical Volume Descriptor
         */
        if (v->lvd_len != 0) {
                off = v->lvd_loc * udfs->lbsize;
                if (ud_read_dev(h->udh, off, buf, v->lvd_len) != 0) {
                        ret = FSTYP_ERR_IO;
                        goto out;
                }

                /* LINTED */
                print_lvd(fout, (struct log_vol_desc *)buf);
        }

        /*
         * Unallocated Space Descriptor
         */
        if (v->usd_len != 0) {
                off = v->usd_loc * udfs->lbsize;
                if (ud_read_dev(h->udh, off, buf, v->usd_len) != 0) {
                        ret = FSTYP_ERR_IO;
                        goto out;
                }

                /* LINTED */
                print_usd(fout, (struct unall_spc_desc *)buf);
        }

        /*
         * Logical Volume Integrity Descriptor
         */
        if (udfs->lvid_len != 0) {
                off = udfs->lvid_loc * udfs->lbsize;
                if (ud_read_dev(h->udh, off, buf, udfs->lvid_len) != 0) {
                        ret = FSTYP_ERR_IO;
                        goto out;
                }

                /* LINTED */
                print_lvid(fout, (struct log_vol_int_desc *)buf);
        }

        /*
         * File Set Descriptor
         */
        if (udfs->fsd_len != 0) {
                off = udfs->fsd_loc * udfs->lbsize;
                if (ud_read_dev(h->udh, off, buf, udfs->fsd_len) != 0) {
                        ret = FSTYP_ERR_IO;
                        goto out;
                }

                /* LINTED */
                print_fsd(fout, h->udh, (struct file_set_desc *)buf);
        }
        ret = 0;

out:
        free(buf);
        return (ret);
}