diff --git a/MAINTAINERS b/MAINTAINERS index 553a8e65dfce..41a7882fc468 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -93,6 +93,7 @@ contrib/pf mlaier Pre-commit review requested. binutils obrien Insists on BU blocked from unapproved commits geom_concat pjd Pre-commit review requested. geom_gate pjd Pre-commit review requested. +geom_label pjd Pre-commit review requested. geom_nop pjd Pre-commit review requested. geom_stripe pjd Pre-commit review requested. sbin/geom pjd Pre-commit review requested. diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist index 590696fcb842..f9a0f7ab2a1d 100644 --- a/etc/mtree/BSD.include.dist +++ b/etc/mtree/BSD.include.dist @@ -84,6 +84,8 @@ .. gate .. + label + .. nop .. stripe diff --git a/include/Makefile b/include/Makefile index f4d78dff2679..130e2266b258 100644 --- a/include/Makefile +++ b/include/Makefile @@ -35,7 +35,7 @@ LSUBDIRS= cam/scsi dev/an dev/bktr dev/firewire dev/ic dev/iicbus \ dev/ofw dev/ppbus dev/smbus dev/usb dev/wi dev/utopia fs/devfs \ fs/fdescfs fs/fifofs fs/msdosfs fs/ntfs fs/nullfs fs/nwfs fs/portalfs \ fs/procfs fs/smbfs fs/udf fs/umapfs fs/unionfs \ - geom/concat geom/gate geom/nop geom/stripe \ + geom/concat geom/gate geom/label geom/nop geom/stripe \ isofs/cd9660 netatm/ipatm netatm/sigpvc netatm/spans netatm/uni \ netgraph/atm security/mac_biba security/mac_bsdextended \ security/mac_lomac security/mac_mls security/mac_partition \ diff --git a/sbin/geom/class/Makefile b/sbin/geom/class/Makefile index 71edcf7c0b25..704466159ba1 100644 --- a/sbin/geom/class/Makefile +++ b/sbin/geom/class/Makefile @@ -1,6 +1,7 @@ # $FreeBSD$ SUBDIR= concat +SUBDIR+=label SUBDIR+=nop SUBDIR+=stripe diff --git a/sbin/geom/class/label/Makefile b/sbin/geom/class/label/Makefile new file mode 100644 index 000000000000..cba7ac6b4483 --- /dev/null +++ b/sbin/geom/class/label/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../misc + +CLASS= label + +.include diff --git a/sbin/geom/class/label/geom_label.c b/sbin/geom/class/label/geom_label.c new file mode 100644 index 000000000000..bbd6e7ff176b --- /dev/null +++ b/sbin/geom/class/label/geom_label.c @@ -0,0 +1,187 @@ +/*- + * Copyright (c) 2004 Pawel Jakub Dawidek + * 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 AUTHORS 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 AUTHORS 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core/geom.h" +#include "misc/subr.h" + + +uint32_t lib_version = G_LIB_VERSION; +uint32_t version = G_LABEL_VERSION; + +static void label_main(struct gctl_req *req, unsigned flags); +static void label_label(struct gctl_req *req); +static void label_clear(struct gctl_req *req); + +struct g_command class_commands[] = { + { "create", G_FLAG_VERBOSE | G_FLAG_LOADKLD, NULL, G_NULL_OPTS }, + { "destroy", G_FLAG_VERBOSE, NULL, + { + { 'f', "force", NULL, G_TYPE_NONE }, + G_OPT_SENTINEL + } + }, + { "label", G_FLAG_VERBOSE | G_FLAG_LOADKLD, label_main, G_NULL_OPTS }, + { "clear", G_FLAG_VERBOSE, label_main, G_NULL_OPTS }, + G_CMD_SENTINEL +}; + +static int verbose = 0; + + +void usage(const char *name); +void +usage(const char *name) +{ + + fprintf(stderr, "usage: %s create [-v] \n", name); + fprintf(stderr, " %s destroy [-fv] [name2 [...]]\n", name); + fprintf(stderr, " %s label [-v] \n", name); + fprintf(stderr, " %s clear [-v] [dev2 [...]]\n", name); +} + +static void +label_main(struct gctl_req *req, unsigned flags) +{ + const char *name; + + if ((flags & G_FLAG_VERBOSE) != 0) + verbose = 1; + + name = gctl_get_asciiparam(req, "verb"); + if (name == NULL) { + gctl_error(req, "No '%s' argument.", "verb"); + return; + } + if (strcmp(name, "label") == 0) + label_label(req); + else if (strcmp(name, "clear") == 0) + label_clear(req); + else + gctl_error(req, "Unknown command: %s.", name); +} + +static void +label_label(struct gctl_req *req) +{ + struct g_label_metadata md; + const char *name, *label; + u_char sector[512]; + int *nargs, error; + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs != 2) { + gctl_error(req, "Invalid number of arguments."); + return; + } + + /* + * Clear last sector first to spoil all components if device exists. + */ + name = gctl_get_asciiparam(req, "arg1"); + if (name == NULL) { + gctl_error(req, "No 'arg%u' argument.", 1); + return; + } + error = g_metadata_clear(name, NULL); + if (error != 0) { + gctl_error(req, "Can't store metadata on %s: %s.", name, + strerror(error)); + return; + } + + strlcpy(md.md_magic, G_LABEL_MAGIC, sizeof(md.md_magic)); + md.md_version = G_LABEL_VERSION; + label = gctl_get_asciiparam(req, "arg0"); + if (label == NULL) { + gctl_error(req, "No 'arg%u' argument.", 0); + return; + } + strlcpy(md.md_label, label, sizeof(md.md_label)); + + /* + * Ok, store metadata. + */ + label_metadata_encode(&md, sector); + error = g_metadata_store(name, sector, sizeof(sector)); + if (error != 0) { + fprintf(stderr, "Can't store metadata on %s: %s.\n", name, + strerror(error)); + gctl_error(req, "Not done."); + } + if (verbose) + printf("Metadata value stored on %s.\n", name); +} + +static void +label_clear(struct gctl_req *req) +{ + const char *name; + char param[16]; + unsigned i; + int *nargs, error; + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument.", "nargs"); + return; + } + if (*nargs < 1) { + gctl_error(req, "Too few arguments."); + return; + } + + for (i = 0; i < (unsigned)*nargs; i++) { + snprintf(param, sizeof(param), "arg%u", i); + name = gctl_get_asciiparam(req, param); + + error = g_metadata_clear(name, G_LABEL_MAGIC); + if (error != 0) { + fprintf(stderr, "Can't clear metadata on %s: %s.\n", + name, strerror(error)); + gctl_error(req, "Not fully done."); + continue; + } + if (verbose) + printf("Metadata cleared on %s.\n", name); + } +} diff --git a/sbin/geom/class/label/glabel.8 b/sbin/geom/class/label/glabel.8 new file mode 100644 index 000000000000..8f94505c10b6 --- /dev/null +++ b/sbin/geom/class/label/glabel.8 @@ -0,0 +1,182 @@ +.\" Copyright (c) 2004 Pawel Jakub Dawidek +.\" 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 AUTHORS 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 AUTHORS 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. +.\" +.\" $FreeBSD$ +.\" +.Dd Jul 2, 2004 +.Dt GLABEL 8 +.Os +.Sh NAME +.Nm glabel +.Nd "disk labelization control utility" +.Sh SYNOPSIS +.Nm +.Cm create +.Op Fl v +.Ar name +.Ar dev +.Nm +.Cm label +.Op Fl v +.Ar name +.Ar dev +.Nm +.Cm destroy +.Op Fl fv +.Ar name +.Op Ar name Op Ar ... +.Nm +.Cm clear +.Op Fl v +.Ar dev +.Op Ar dev Op Ar ... +.Nm +.Cm list +.Op Ar prov Op Ar ... +.Nm +.Cm load +.Op Fl v +.Nm +.Cm unload +.Op Fl v +.Sh DESCRIPTION +The +.Nm +utility is used for GEOM providers labelization. +A label can be setup on a GEOM provider in two ways: +.Dq manual +or +.Dq automatic . +When using the +.Dq manual +method, no metadata are stored on the devices, so a label has to be configured +by hand every time it is needed. +The +.Dq automatic +method use on-disk metadata to store label and detect it automatically in the +future. +.Pp +This class also provide volume label detection for file systems. +Those labels cannot be set with +.Nm , +but must be set with the appropriate file system utility, e.g. for UFS +the file system label is set with +.Xr tunefs 8 . +Currently supported file systems are: +.Pp +.Bl -bullet -offset indent -compact +.It +UFS1 (directory +.Pa /dev/ufs/ ) . +.It +UFS2 (directory +.Pa /dev/ufs/ ) . +.It +MSDOSFS (FAT12, FAT16, FAT32) (directory +.Pa /dev/msdosfs/ ) . +.It +CD ISO9660 (directory +.Pa /dev/iso9660/ ) . +.El +.Pp +Non file-system labels are created in the directory +.Pa /dev/label/ . +.Pp +The first argument to +.Nm +indicates an action to be performed: +.Bl -tag -width ".Cm destroy" +.It Cm create +Create temporary label +.Ar name +for the given provider. +This is the +.Dq manual +method. +The kernel module +.Pa geom_label.ko +will be loaded if it is not loaded already. +.It Cm label +Setup a label +.Ar name +for the given provider. +This is the +.Dq automatic +method, where metadata are stored in provider's last sector. +The kernel module +.Pa geom_label.ko +will be loaded if it is not loaded already. +.It Cm destroy +Turn off the given labels by its +.Ar name . +.It Cm clear +Clear metadata on the given devices. +.It Cm list +List all currently configured devices, or list the given devices. +.It Cm load +Load +.Pa geom_label.ko +kernel module. +.It Cm unload +Unload +.Pa geom_label.ko +kernel module. +.El +.Pp +Additional options: +.Bl -tag -width indent +.It Fl f +Force the removal of the specified labels. +.It Fl v +Be more verbose. +.El +.Sh EXAMPLES +The following example shows how to setup a label for disk da2, +create a file system on it, and mount it: +.Bd -literal -offset indent +glabel label -v usr /dev/da2 +newfs /dev/label/usr +mount /dev/label/usr /usr +.Ed +.Pp +The next example shows how to setup a label for a UFS file system: +.Bd -literal -offset indent +tunefs -L data /dev/da4s1a +mount /dev/ufs/data /mnt/data +.Ed +.Sh DIAGNOSTICS +Exit status is 0 on success, and 1 if the command fails. +.Sh SEE ALSO +.Xr geom 4 , +.Xr geom 8 , +.Xr mount 8 , +.Xr newfs 8 , +.Xr tunefs 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Fx 5.3 . +.Sh AUTHORS +.An Pawel Jakub Dawidek Aq pjd@FreeBSD.org diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 2191e572bb29..260714298879 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -125,6 +125,7 @@ options GEOM_CONCAT # Disk concatenation. options GEOM_FOX # Redundant path mitigation options GEOM_GATE # Userland services. options GEOM_GPT # GPT partitioning +options GEOM_LABEL # Providers labelization. options GEOM_MBR # DOS/MBR partitioning options GEOM_NOP # Test class. options GEOM_PC98 # NEC PC9800 partitioning diff --git a/sys/conf/files b/sys/conf/files index 10c2df72614e..a25b963cfeac 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -940,6 +940,10 @@ crypto/rijndael/rijndael-api-fst.c optional geom_bde crypto/sha2/sha2.c optional geom_bde geom/concat/g_concat.c optional geom_concat geom/gate/g_gate.c optional geom_gate +geom/label/g_label.c optional geom_label +geom/label/g_label_iso9660.c optional geom_label +geom/label/g_label_msdosfs.c optional geom_label +geom/label/g_label_ufs.c optional geom_label geom/nop/g_nop.c optional geom_nop geom/stripe/g_stripe.c optional geom_stripe geom/geom_aes.c optional geom_aes diff --git a/sys/conf/options b/sys/conf/options index 1c6c146d966b..d086703abfac 100644 --- a/sys/conf/options +++ b/sys/conf/options @@ -71,6 +71,7 @@ GEOM_CONCAT opt_geom.h GEOM_FOX opt_geom.h GEOM_GATE opt_geom.h GEOM_GPT opt_geom.h +GEOM_LABEL opt_geom.h GEOM_MBR opt_geom.h GEOM_MIRROR opt_geom.h GEOM_NOP opt_geom.h diff --git a/sys/geom/label/g_label.c b/sys/geom/label/g_label.c new file mode 100644 index 000000000000..2aa9754b70d3 --- /dev/null +++ b/sys/geom/label/g_label.c @@ -0,0 +1,405 @@ +/*- + * Copyright (c) 2004 Pawel Jakub Dawidek + * 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 AUTHORS 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 AUTHORS 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +SYSCTL_DECL(_kern_geom); +SYSCTL_NODE(_kern_geom, OID_AUTO, label, CTLFLAG_RW, 0, "GEOM_LABEL stuff"); +u_int g_label_debug = 0; +SYSCTL_UINT(_kern_geom_label, OID_AUTO, debug, CTLFLAG_RW, &g_label_debug, 0, + "Debug level"); + +static int g_label_destroy(struct g_geom *gp, boolean_t force); +static struct g_geom *g_label_taste(struct g_class *mp, struct g_provider *pp, + int flags __unused); +static void g_label_config(struct gctl_req *req, struct g_class *mp, + const char *verb); + +struct g_class g_label_class = { + .name = G_LABEL_CLASS_NAME, + .ctlreq = g_label_config, + .taste = g_label_taste +}; + +/* + * To add a new file system where you want to look for volume labels, + * you have to: + * 1. Add a file which implements looking for volume labels. + * 2. Add an 'extern const struct g_label_desc g_label_;'. + * 3. Add an element to the table below '&g_label_,'. + */ +const struct g_label_desc *g_labels[] = { + &g_label_ufs, + &g_label_iso9660, + &g_label_msdosfs, + NULL +}; + + +static void +g_label_orphan(struct g_consumer *cp __unused) +{ + + KASSERT(1 == 0, ("%s called?", __func__)); +} + +static void +g_label_start(struct bio *bp __unused) +{ + + KASSERT(1 == 0, ("%s called?", __func__)); +} + +static int +g_label_access(struct g_provider *pp __unused, int dr __unused, int dw __unused, + int de __unused) +{ + + KASSERT(1 == 0, ("%s called", __func__)); + return (EOPNOTSUPP); +} + +static struct g_geom * +g_label_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp, + const char *label, const char *dir, off_t mediasize) +{ + struct g_geom *gp; + struct g_provider *pp2; + struct g_consumer *cp; + char name[64]; + + g_topology_assert(); + + gp = NULL; + cp = NULL; + snprintf(name, sizeof(name), "%s/%s", dir, label); + LIST_FOREACH(gp, &mp->geom, geom) { + pp2 = LIST_FIRST(&gp->provider); + if (pp2 == NULL) + continue; + if (strcmp(pp2->name, name) == 0) { + G_LABEL_DEBUG(1, "Label %s(%s) already exists (%s).", + label, name, pp->name); + if (req != NULL) { + gctl_error(req, "Provider %s already exists.", + name); + } + return (NULL); + } + } + gp = g_slice_new(mp, 1, pp, &cp, NULL, 0, NULL); + if (gp == NULL) { + G_LABEL_DEBUG(0, "Cannot create slice %s.", label); + if (req != NULL) + gctl_error(req, "Cannot create slice %s.", label); + return (NULL); + } + g_access(cp, -1, 0, 0); + g_slice_config(gp, 0, G_SLICE_CONFIG_SET, (off_t)0, mediasize, + pp->sectorsize, name); + G_LABEL_DEBUG(0, "Label for provider %s is %s.", pp->name, name); + return (gp); +} + +static int +g_label_destroy(struct g_geom *gp, boolean_t force) +{ + struct g_provider *pp; + + g_topology_assert(); + pp = LIST_FIRST(&gp->provider); + if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { + if (force) { + G_LABEL_DEBUG(0, "Provider %s is still open, so it " + "can't be definitely removed.", pp->name); + } else { + G_LABEL_DEBUG(1, + "Provider %s is still open (r%dw%de%d).", pp->name, + pp->acr, pp->acw, pp->ace); + return (EBUSY); + } + } else { + G_LABEL_DEBUG(0, "Label %s removed.", + LIST_FIRST(&gp->provider)->name); + } + g_slice_spoiled(LIST_FIRST(&gp->consumer)); + return (0); +} + +static int +g_label_read_metadata(struct g_consumer *cp, struct g_label_metadata *md) +{ + struct g_provider *pp; + u_char *buf; + int error; + + g_topology_assert(); + + pp = cp->provider; + g_topology_unlock(); + buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, + &error); + g_topology_lock(); + if (buf == NULL) + return (error); + /* Decode metadata. */ + label_metadata_decode(buf, md); + g_free(buf); + + return (0); +} + +static struct g_geom * +g_label_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) +{ + struct g_label_metadata md; + struct g_consumer *cp; + struct g_geom *gp; + int error, i; + + g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); + g_topology_assert(); + + G_LABEL_DEBUG(3, "Tasting %s.", pp->name); + + if (strcmp(pp->geom->class->name, mp->name) == 0) + return (NULL); + + gp = g_new_geomf(mp, "label:taste"); + gp->start = g_label_start; + gp->access = g_label_access; + gp->orphan = g_label_orphan; + cp = g_new_consumer(gp); + g_attach(cp, pp); + error = g_access(cp, 1, 0, 0); + if (error != 0) + return (NULL); + + do { + error = g_label_read_metadata(cp, &md); + if (error != 0) + break; + if (strcmp(md.md_magic, G_LABEL_MAGIC) != 0) + break; + if (md.md_version > G_LABEL_VERSION) { + printf("geom_label.ko module is too old to handle %s.\n", + pp->name); + break; + } + g_label_create(NULL, mp, pp, md.md_label, G_LABEL_DIR, + pp->mediasize - pp->sectorsize); + } while (0); + for (i = 0; g_labels[i] != NULL; i++) { + char label[64]; + + g_topology_unlock(); + g_labels[i]->ld_taste(cp, label, sizeof(label)); + g_topology_lock(); + if (label[0] == '\0') + continue; + g_label_create(NULL, mp, pp, label, g_labels[i]->ld_dir, + pp->mediasize); + } + + g_access(cp, -1, 0, 0); + g_wither_geom(gp, ENXIO); + + return (NULL); +} + +static void +g_label_ctl_create(struct gctl_req *req, struct g_class *mp) +{ + struct g_provider *pp; + const char *name; + int *nargs; + + g_topology_assert(); + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument", "nargs"); + return; + } + if (*nargs != 2) { + gctl_error(req, "Invalid number of argument."); + return; + } + /* + * arg1 is the name of provider. + */ + name = gctl_get_asciiparam(req, "arg1"); + if (name == NULL) { + gctl_error(req, "No 'arg%d' argument", 1); + return; + } + if (strncmp(name, "/dev/", strlen("/dev/")) == 0) + name += strlen("/dev/"); + pp = g_provider_by_name(name); + if (pp == NULL) { + G_LABEL_DEBUG(1, "Provider %s is invalid.", name); + gctl_error(req, "Provider %s is invalid.", name); + return; + } + /* + * arg0 is the label. + */ + name = gctl_get_asciiparam(req, "arg0"); + if (name == NULL) { + gctl_error(req, "No 'arg%d' argument", 0); + return; + } + g_label_create(req, mp, pp, name, G_LABEL_DIR, pp->mediasize); +} + +static const char * +g_label_skip_dir(const char *name) +{ + char path[64]; + u_int i; + + if (strncmp(name, "/dev/", strlen("/dev/")) == 0) + name += strlen("/dev/"); + if (strncmp(name, G_LABEL_DIR "/", strlen(G_LABEL_DIR "/")) == 0) + name += strlen(G_LABEL_DIR "/"); + for (i = 0; g_labels[i] != NULL; i++) { + snprintf(path, sizeof(path), "%s/", g_labels[i]->ld_dir); + if (strncmp(name, path, strlen(path)) == 0) { + name += strlen(path); + break; + } + } + return (name); +} + +static struct g_geom * +g_label_find_geom(struct g_class *mp, const char *name) +{ + struct g_geom *gp; + struct g_provider *pp; + const char *pname; + + name = g_label_skip_dir(name); + LIST_FOREACH(gp, &mp->geom, geom) { + pp = LIST_FIRST(&gp->provider); + pname = g_label_skip_dir(pp->name); + if (strcmp(pname, name) == 0) + return (gp); + } + return (NULL); +} + +static void +g_label_ctl_destroy(struct gctl_req *req, struct g_class *mp) +{ + int *nargs, *force, error, i; + struct g_geom *gp; + const char *name; + char param[16]; + + g_topology_assert(); + + nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); + if (nargs == NULL) { + gctl_error(req, "No '%s' argument", "nargs"); + return; + } + if (*nargs <= 0) { + gctl_error(req, "Missing device(s)."); + return; + } + force = gctl_get_paraml(req, "force", sizeof(*force)); + if (force == NULL) { + gctl_error(req, "No 'force' argument"); + return; + } + + for (i = 0; i < *nargs; i++) { + snprintf(param, sizeof(param), "arg%d", i); + name = gctl_get_asciiparam(req, param); + if (name == NULL) { + gctl_error(req, "No 'arg%d' argument", i); + return; + } + gp = g_label_find_geom(mp, name); + if (gp == NULL) { + G_LABEL_DEBUG(1, "Label %s is invalid.", name); + gctl_error(req, "Label %s is invalid.", name); + return; + } + error = g_label_destroy(gp, *force); + if (error != 0) { + gctl_error(req, "Cannot destroy label %s (error=%d).", + LIST_FIRST(&gp->provider)->name, error); + return; + } + } +} + +static void +g_label_config(struct gctl_req *req, struct g_class *mp, const char *verb) +{ + uint32_t *version; + + g_topology_assert(); + + version = gctl_get_paraml(req, "version", sizeof(*version)); + if (version == NULL) { + gctl_error(req, "No '%s' argument.", "version"); + return; + } + if (*version != G_LABEL_VERSION) { + gctl_error(req, "Userland and kernel parts are out of sync."); + return; + } + + if (strcmp(verb, "create") == 0) { + g_label_ctl_create(req, mp); + return; + } else if (strcmp(verb, "destroy") == 0) { + g_label_ctl_destroy(req, mp); + return; + } + + gctl_error(req, "Unknown verb."); +} + +DECLARE_GEOM_CLASS(g_label_class, g_label); diff --git a/sys/geom/label/g_label.h b/sys/geom/label/g_label.h new file mode 100644 index 000000000000..011cc5e3ba5e --- /dev/null +++ b/sys/geom/label/g_label.h @@ -0,0 +1,97 @@ +/*- + * Copyright (c) 2004 Pawel Jakub Dawidek + * 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 AUTHORS 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 AUTHORS 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. + * + * $FreeBSD$ + */ + +#ifndef _G_LABEL_H_ +#define _G_LABEL_H_ + +#include + +#define G_LABEL_CLASS_NAME "LABEL" + +#define G_LABEL_MAGIC "GEOM::LABEL" +#define G_LABEL_VERSION 0 +#define G_LABEL_DIR "label" + +#ifdef _KERNEL +extern u_int g_label_debug; + +#define G_LABEL_DEBUG(lvl, ...) do { \ + if (g_label_debug >= (lvl)) { \ + printf("GEOM_LABEL"); \ + if (g_label_debug > 0) \ + printf("[%u]", lvl); \ + printf(": "); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + } \ +} while (0) +#define G_LABEL_LOGREQ(bp, ...) do { \ + if (g_label_debug >= 2) { \ + printf("GEOM_LABEL[2]: "); \ + printf(__VA_ARGS__); \ + printf(" "); \ + g_print_bio(bp); \ + printf("\n"); \ + } \ +} while (0) + +typedef void g_label_taste_t (struct g_consumer *cp, char *label, size_t size); + +struct g_label_desc { + g_label_taste_t *ld_taste; + char *ld_dir; +}; + +/* Supported labels. */ +extern const struct g_label_desc g_label_ufs; +extern const struct g_label_desc g_label_iso9660; +extern const struct g_label_desc g_label_msdosfs; +#endif /* _KERNEL */ + +struct g_label_metadata { + char md_magic[16]; /* Magic value. */ + uint32_t md_version; /* Version number. */ + char md_label[16]; /* Label. */ +}; +static __inline void +label_metadata_encode(const struct g_label_metadata *md, u_char *data) +{ + + bcopy(md->md_magic, data, sizeof(md->md_magic)); + le32enc(data + 16, md->md_version); + bcopy(md->md_label, data + 20, sizeof(md->md_label)); +} +static __inline void +label_metadata_decode(const u_char *data, struct g_label_metadata *md) +{ + + bcopy(data, md->md_magic, sizeof(md->md_magic)); + md->md_version = le32dec(data + 16); + bcopy(data + 20, md->md_label, sizeof(md->md_label)); +} +#endif /* _G_LABEL_H_ */ diff --git a/sys/geom/label/g_label_iso9660.c b/sys/geom/label/g_label_iso9660.c new file mode 100644 index 000000000000..34d9252bdfdb --- /dev/null +++ b/sys/geom/label/g_label_iso9660.c @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 2004 Pawel Jakub Dawidek + * 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 AUTHORS 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 AUTHORS 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include + +#define G_LABEL_ISO9660_DIR "iso9660" + +#define ISO9660_MAGIC "\x01" "CD001" "\x01\x00" +#define VOLUME_LEN 32 + + +static void +g_label_iso9660_taste(struct g_consumer *cp, char *label, size_t size) +{ + struct g_provider *pp; + char *sector, *volume; + int i, error; + + g_topology_assert_not(); + pp = cp->provider; + label[0] = '\0'; + + sector = (char *)g_read_data(cp, 0x8000, pp->sectorsize, &error); + if (sector == NULL || error != 0) + return; + if (bcmp(sector, ISO9660_MAGIC, sizeof(ISO9660_MAGIC) - 1) != 0) { + g_free(sector); + return; + } + G_LABEL_DEBUG(1, "ISO9660 file system detected on %s.", pp->name); + volume = sector + 0x28; + bzero(label, size); + strlcpy(label, volume, MIN(size, VOLUME_LEN)); + g_free(sector); + for (i = size - 1; i > 0; i--) { + if (label[i] == '\0') + continue; + else if (label[i] == ' ') + label[i] = '\0'; + else + break; + } +} + +const struct g_label_desc g_label_iso9660 = { + .ld_taste = g_label_iso9660_taste, + .ld_dir = G_LABEL_ISO9660_DIR +}; diff --git a/sys/geom/label/g_label_msdosfs.c b/sys/geom/label/g_label_msdosfs.c new file mode 100644 index 000000000000..ff0d7c3b7e14 --- /dev/null +++ b/sys/geom/label/g_label_msdosfs.c @@ -0,0 +1,101 @@ +/*- + * Copyright (c) 2004 Pawel Jakub Dawidek + * 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 AUTHORS 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 AUTHORS 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include + +#define G_LABEL_MSDOSFS_DIR "msdosfs" + +#define FAT12 "FAT12 " +#define FAT16 "FAT16 " +#define FAT32 "FAT32 " +#define VOLUME_LEN 11 +#define NO_NAME "NO NAME " + + +static void +g_label_msdosfs_taste(struct g_consumer *cp, char *label, size_t size) +{ + struct g_provider *pp; + char *sector, *volume; + int i, error; + + g_topology_assert_not(); + pp = cp->provider; + label[0] = '\0'; + + sector = (char *)g_read_data(cp, 0, pp->sectorsize, &error); + if (sector == NULL || error != 0) + return; + if (strncmp(sector + 0x36, FAT12, strlen(FAT12)) == 0) { + G_LABEL_DEBUG(1, "MSDOS (FAT12) file system detected on %s.", + pp->name); + volume = sector + 0x2b; + } else if (strncmp(sector + 0x36, FAT16, strlen(FAT16)) == 0) { + G_LABEL_DEBUG(1, "MSDOS (FAT16) file system detected on %s.", + pp->name); + volume = sector + 0x2b; + } else if (strncmp(sector + 0x52, FAT32, strlen(FAT32)) == 0) { + G_LABEL_DEBUG(1, "MSDOS (FAT32) file system detected on %s.", + pp->name); + volume = sector + 0x47; + } else { + g_free(sector); + return; + } + if (strncmp(volume, NO_NAME, VOLUME_LEN) == 0) { + g_free(sector); + return; + } + if (volume[0] == '\0') { + g_free(sector); + return; + } + bzero(label, size); + strlcpy(label, volume, MIN(size, VOLUME_LEN)); + g_free(sector); + for (i = size - 1; i > 0; i--) { + if (label[i] == '\0') + continue; + else if (label[i] == ' ') + label[i] = '\0'; + else + break; + } +} + +const struct g_label_desc g_label_msdosfs = { + .ld_taste = g_label_msdosfs_taste, + .ld_dir = G_LABEL_MSDOSFS_DIR +}; diff --git a/sys/geom/label/g_label_ufs.c b/sys/geom/label/g_label_ufs.c new file mode 100644 index 000000000000..dc188f7a6939 --- /dev/null +++ b/sys/geom/label/g_label_ufs.c @@ -0,0 +1,102 @@ +/*- + * Copyright (c) 2002, 2003 Gordon Tetlow + * 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 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#define G_LABEL_UFS_DIR "ufs" + +static const int superblocks[] = SBLOCKSEARCH; + +static void +g_label_ufs_taste(struct g_consumer *cp, char *label, size_t size) +{ + struct g_provider *pp; + int error, sb, superblock; + struct fs *fs; + + g_topology_assert_not(); + pp = cp->provider; + label[0] = '\0'; + /* + * Walk through the standard places that superblocks hide and look + * for UFS magic. If we find magic, then check that the size in the + * superblock corresponds to the size of the underlying provider. + * Finally, look for a volume label and create an appropriate + * provider based on that. + */ + for (sb = 0; (superblock = superblocks[sb]) != -1; sb++) { + fs = (struct fs *)g_read_data(cp, superblock, SBLOCKSIZE, + &error); + if (fs == NULL || error != 0) + continue; + /* Check for magic and make sure things are the right size */ + if (fs->fs_magic == FS_UFS1_MAGIC) { + G_LABEL_DEBUG(1, "UFS1 file system detected on %s.", + pp->name); + if (fs->fs_old_size * fs->fs_fsize != + (int32_t)pp->mediasize) { + g_free(fs); + continue; + } + } else if (fs->fs_magic == FS_UFS2_MAGIC) { + G_LABEL_DEBUG(1, "UFS2 file system detected on %s.", + pp->name); + if (fs->fs_size * fs->fs_fsize != + (int64_t)pp->mediasize) { + g_free(fs); + continue; + } + } else { + g_free(fs); + continue; + } + /* Check for volume label */ + if (fs->fs_volname[0] == '\0') { + g_free(fs); + continue; + } + strlcpy(label, fs->fs_volname, size); + g_free(fs); + break; + } +} + +const struct g_label_desc g_label_ufs = { + .ld_taste = g_label_ufs_taste, + .ld_dir = G_LABEL_UFS_DIR +}; diff --git a/sys/modules/geom/Makefile b/sys/modules/geom/Makefile index 039ae29145f1..880c082902c1 100644 --- a/sys/modules/geom/Makefile +++ b/sys/modules/geom/Makefile @@ -8,6 +8,7 @@ SUBDIR= geom_apple \ geom_fox \ geom_gate \ geom_gpt \ + geom_label \ geom_mbr \ geom_nop \ geom_pc98 \ diff --git a/sys/modules/geom/geom_label/Makefile b/sys/modules/geom/geom_label/Makefile new file mode 100644 index 000000000000..839a48ef17fb --- /dev/null +++ b/sys/modules/geom/geom_label/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../geom/label + +KMOD= geom_label +SRCS= g_label.c +SRCS+= g_label_iso9660.c +SRCS+= g_label_msdosfs.c +SRCS+= g_label_ufs.c + +.include