Changeset View
Changeset View
Standalone View
Standalone View
sys/geom/label/g_label.c
Show First 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | |||||
SYSCTL_UINT(_kern_geom_label, OID_AUTO, debug, CTLFLAG_RWTUN, &g_label_debug, 0, | SYSCTL_UINT(_kern_geom_label, OID_AUTO, debug, CTLFLAG_RWTUN, &g_label_debug, 0, | ||||
"Debug level"); | "Debug level"); | ||||
static int g_label_destroy_geom(struct gctl_req *req, struct g_class *mp, | static int g_label_destroy_geom(struct gctl_req *req, struct g_class *mp, | ||||
struct g_geom *gp); | struct g_geom *gp); | ||||
static int g_label_destroy(struct g_geom *gp, boolean_t force); | 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, | static struct g_geom *g_label_taste(struct g_class *mp, struct g_provider *pp, | ||||
int flags __unused); | int flags __unused); | ||||
static void g_label_generic_taste(struct g_consumer *, char *, size_t); | |||||
static void g_label_config(struct gctl_req *req, struct g_class *mp, | static void g_label_config(struct gctl_req *req, struct g_class *mp, | ||||
const char *verb); | const char *verb); | ||||
#define G_LABEL_DIRPREFIX "label/" | |||||
struct g_class g_label_class = { | struct g_class g_label_class = { | ||||
.name = G_LABEL_CLASS_NAME, | .name = G_LABEL_CLASS_NAME, | ||||
.version = G_VERSION, | .version = G_VERSION, | ||||
.ctlreq = g_label_config, | .ctlreq = g_label_config, | ||||
.taste = g_label_taste, | .taste = g_label_taste, | ||||
.destroy_geom = g_label_destroy_geom | .destroy_geom = g_label_destroy_geom | ||||
}; | }; | ||||
static struct g_label_desc g_label_generic = { | |||||
.ld_taste = g_label_generic_taste, | |||||
.ld_dirprefix = G_LABEL_DIRPREFIX, | |||||
.ld_enabled = 1 | |||||
}; | |||||
/* | /* | ||||
* To add a new file system where you want to look for volume labels, | * To add a new file system where you want to look for volume labels, | ||||
* you have to: | * you have to: | ||||
* 1. Add a file g_label_<file system>.c which implements labels recognition. | * 1. Add a file g_label_<file system>.c which implements labels recognition. | ||||
* 2. Add an 'extern const struct g_label_desc g_label_<file system>;' into | * 2. Add an 'extern const struct g_label_desc g_label_<file system>;' into | ||||
* g_label.h file. | * g_label.h file. | ||||
* 3. Add an element to the table below '&g_label_<file system>,'. | * 3. Add an element to the table below '&g_label_<file system>,'. | ||||
* 4. Add your file to sys/conf/files. | * 4. Add your file to sys/conf/files. | ||||
Show All 9 Lines | #ifdef GEOM_LABEL | ||||
&g_label_iso9660, | &g_label_iso9660, | ||||
&g_label_msdosfs, | &g_label_msdosfs, | ||||
&g_label_ext2fs, | &g_label_ext2fs, | ||||
&g_label_reiserfs, | &g_label_reiserfs, | ||||
&g_label_ntfs, | &g_label_ntfs, | ||||
&g_label_disk_ident, | &g_label_disk_ident, | ||||
&g_label_flashmap, | &g_label_flashmap, | ||||
#endif | #endif | ||||
&g_label_generic, | |||||
NULL | NULL | ||||
}; | }; | ||||
void | void | ||||
g_label_rtrim(char *label, size_t size) | g_label_rtrim(char *label, size_t size) | ||||
{ | { | ||||
ptrdiff_t i; | ptrdiff_t i; | ||||
▲ Show 20 Lines • Show All 98 Lines • ▼ Show 20 Lines | if (sbuf_finish(sb) != 0) | ||||
label[0] = '\0'; | label[0] = '\0'; | ||||
else | else | ||||
strlcpy(label, sbuf_data(sb), size); | strlcpy(label, sbuf_data(sb), size); | ||||
sbuf_delete(sb); | sbuf_delete(sb); | ||||
} | } | ||||
static struct g_geom * | static struct g_geom * | ||||
g_label_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp, | g_label_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp, | ||||
const char *label, const char *dir, off_t mediasize) | const char *label, const char *dirprefix, off_t mediasize) | ||||
{ | { | ||||
struct g_geom *gp; | struct g_geom *gp; | ||||
struct g_provider *pp2; | struct g_provider *pp2; | ||||
struct g_consumer *cp; | struct g_consumer *cp; | ||||
char name[64]; | char name[64]; | ||||
g_topology_assert(); | g_topology_assert(); | ||||
if (!g_label_is_name_ok(label)) { | if (!g_label_is_name_ok(label)) { | ||||
G_LABEL_DEBUG(0, "%s contains suspicious label, skipping.", | G_LABEL_DEBUG(0, "%s contains suspicious label, skipping.", | ||||
pp->name); | pp->name); | ||||
G_LABEL_DEBUG(1, "%s suspicious label is: %s", pp->name, label); | G_LABEL_DEBUG(1, "%s suspicious label is: %s", pp->name, label); | ||||
if (req != NULL) | if (req != NULL) | ||||
gctl_error(req, "Label name %s is invalid.", label); | gctl_error(req, "Label name %s is invalid.", label); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
gp = NULL; | gp = NULL; | ||||
cp = NULL; | cp = NULL; | ||||
if (snprintf(name, sizeof(name), "%s/%s", dir, label) >= sizeof(name)) { | if (snprintf(name, sizeof(name), "%s%s", dirprefix, label) >= sizeof(name)) { | ||||
if (req != NULL) | if (req != NULL) | ||||
gctl_error(req, "Label name %s is too long.", label); | gctl_error(req, "Label name %s is too long.", label); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
LIST_FOREACH(gp, &mp->geom, geom) { | LIST_FOREACH(gp, &mp->geom, geom) { | ||||
pp2 = LIST_FIRST(&gp->provider); | pp2 = LIST_FIRST(&gp->provider); | ||||
if (pp2 == NULL) | if (pp2 == NULL) | ||||
continue; | continue; | ||||
▲ Show 20 Lines • Show All 51 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
g_label_read_metadata(struct g_consumer *cp, struct g_label_metadata *md) | g_label_read_metadata(struct g_consumer *cp, struct g_label_metadata *md) | ||||
{ | { | ||||
struct g_provider *pp; | struct g_provider *pp; | ||||
u_char *buf; | u_char *buf; | ||||
int error; | int error; | ||||
g_topology_assert(); | |||||
pp = cp->provider; | pp = cp->provider; | ||||
g_topology_unlock(); | |||||
buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, | buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, | ||||
&error); | &error); | ||||
g_topology_lock(); | |||||
if (buf == NULL) | if (buf == NULL) | ||||
return (error); | return (error); | ||||
/* Decode metadata. */ | /* Decode metadata. */ | ||||
label_metadata_decode(buf, md); | label_metadata_decode(buf, md); | ||||
g_free(buf); | g_free(buf); | ||||
return (0); | return (0); | ||||
} | } | ||||
Show All 16 Lines | |||||
g_label_access_taste(struct g_provider *pp __unused, int dr __unused, | g_label_access_taste(struct g_provider *pp __unused, int dr __unused, | ||||
int dw __unused, int de __unused) | int dw __unused, int de __unused) | ||||
{ | { | ||||
KASSERT(1 == 0, ("%s called", __func__)); | KASSERT(1 == 0, ("%s called", __func__)); | ||||
return (EOPNOTSUPP); | return (EOPNOTSUPP); | ||||
} | } | ||||
static void | |||||
g_label_generic_taste(struct g_consumer *cp, char *label, size_t size) | |||||
{ | |||||
struct g_provider *pp; | |||||
struct g_label_metadata md; | |||||
g_topology_assert_not(); | |||||
label[0] = '\0'; | |||||
pp = cp->provider; | |||||
if (g_label_read_metadata(cp, &md) != 0) | |||||
return; | |||||
if (strcmp(md.md_magic, G_LABEL_MAGIC) != 0) | |||||
return; | |||||
if (md.md_version > G_LABEL_VERSION) { | |||||
printf("geom_label.ko module is too old to handle %s.\n", | |||||
pp->name); | |||||
return; | |||||
} | |||||
/* | |||||
* Backward compatibility: there was no md_provsize field in | |||||
* earlier versions of metadata, so only check if we have it. | |||||
*/ | |||||
if (md.md_version >= 2 && md.md_provsize != pp->mediasize) | |||||
return; | |||||
strlcpy(label, md.md_label, size); | |||||
} | |||||
static struct g_geom * | static struct g_geom * | ||||
g_label_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) | 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_consumer *cp; | ||||
struct g_geom *gp; | struct g_geom *gp; | ||||
off_t mediasize; | |||||
int i; | int i; | ||||
g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); | g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); | ||||
g_topology_assert(); | g_topology_assert(); | ||||
G_LABEL_DEBUG(2, "Tasting %s.", pp->name); | G_LABEL_DEBUG(2, "Tasting %s.", pp->name); | ||||
/* Skip providers that are already open for writing. */ | /* Skip providers that are already open for writing. */ | ||||
if (pp->acw > 0) | if (pp->acw > 0) | ||||
return (NULL); | return (NULL); | ||||
if (strcmp(pp->geom->class->name, mp->name) == 0) | if (strcmp(pp->geom->class->name, mp->name) == 0) | ||||
return (NULL); | return (NULL); | ||||
gp = g_new_geomf(mp, "label:taste"); | gp = g_new_geomf(mp, "label:taste"); | ||||
gp->start = g_label_start_taste; | gp->start = g_label_start_taste; | ||||
gp->access = g_label_access_taste; | gp->access = g_label_access_taste; | ||||
gp->orphan = g_label_orphan_taste; | gp->orphan = g_label_orphan_taste; | ||||
cp = g_new_consumer(gp); | cp = g_new_consumer(gp); | ||||
g_attach(cp, pp); | g_attach(cp, pp); | ||||
if (g_access(cp, 1, 0, 0) != 0) | if (g_access(cp, 1, 0, 0) != 0) | ||||
goto end; | goto end; | ||||
do { | |||||
if (g_label_read_metadata(cp, &md) != 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; | |||||
} | |||||
/* | |||||
* Backward compatibility: | |||||
*/ | |||||
/* | |||||
* There was no md_provsize field in earlier versions of | |||||
* metadata. | |||||
*/ | |||||
if (md.md_version < 2) | |||||
md.md_provsize = pp->mediasize; | |||||
if (md.md_provsize != pp->mediasize) | |||||
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++) { | for (i = 0; g_labels[i] != NULL; i++) { | ||||
char label[128]; | char label[128]; | ||||
if (g_labels[i]->ld_enabled == 0) | if (g_labels[i]->ld_enabled == 0) | ||||
continue; | continue; | ||||
g_topology_unlock(); | g_topology_unlock(); | ||||
g_labels[i]->ld_taste(cp, label, sizeof(label)); | g_labels[i]->ld_taste(cp, label, sizeof(label)); | ||||
g_label_mangle_name(label, sizeof(label)); | g_label_mangle_name(label, sizeof(label)); | ||||
g_topology_lock(); | g_topology_lock(); | ||||
if (label[0] == '\0') | if (label[0] == '\0') | ||||
continue; | continue; | ||||
g_label_create(NULL, mp, pp, label, g_labels[i]->ld_dir, | if (g_labels[i] != &g_label_generic) { | ||||
pp->mediasize); | mediasize = pp->mediasize; | ||||
} else { | |||||
mediasize = pp->mediasize - pp->sectorsize; | |||||
cem: It might be nice to encode this in the `g_labels[]` table somehow instead. | |||||
} | } | ||||
g_label_create(NULL, mp, pp, label, | |||||
g_labels[i]->ld_dirprefix, mediasize); | |||||
} | |||||
g_access(cp, -1, 0, 0); | g_access(cp, -1, 0, 0); | ||||
end: | end: | ||||
g_detach(cp); | g_detach(cp); | ||||
g_destroy_consumer(cp); | g_destroy_consumer(cp); | ||||
g_destroy_geom(gp); | g_destroy_geom(gp); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
Show All 34 Lines | g_label_ctl_create(struct gctl_req *req, struct g_class *mp) | ||||
/* | /* | ||||
* arg0 is the label. | * arg0 is the label. | ||||
*/ | */ | ||||
name = gctl_get_asciiparam(req, "arg0"); | name = gctl_get_asciiparam(req, "arg0"); | ||||
if (name == NULL) { | if (name == NULL) { | ||||
gctl_error(req, "No 'arg%d' argument", 0); | gctl_error(req, "No 'arg%d' argument", 0); | ||||
return; | return; | ||||
} | } | ||||
g_label_create(req, mp, pp, name, G_LABEL_DIR, pp->mediasize); | g_label_create(req, mp, pp, name, G_LABEL_DIRPREFIX, pp->mediasize); | ||||
} | } | ||||
static const char * | static const char * | ||||
g_label_skip_dir(const char *name) | g_label_skip_dir(const char *name) | ||||
{ | { | ||||
char path[64]; | |||||
u_int i; | u_int i; | ||||
if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) == 0) | if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) == 0) | ||||
name += strlen(_PATH_DEV); | name += strlen(_PATH_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++) { | for (i = 0; g_labels[i] != NULL; i++) { | ||||
snprintf(path, sizeof(path), "%s/", g_labels[i]->ld_dir); | if (strncmp(name, g_labels[i]->ld_dirprefix, | ||||
if (strncmp(name, path, strlen(path)) == 0) { | strlen(g_labels[i]->ld_dirprefix)) == 0) { | ||||
name += strlen(path); | name += strlen(g_labels[i]->ld_dirprefix); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
return (name); | return (name); | ||||
} | } | ||||
static struct g_geom * | static struct g_geom * | ||||
g_label_find_geom(struct g_class *mp, const char *name) | g_label_find_geom(struct g_class *mp, const char *name) | ||||
▲ Show 20 Lines • Show All 93 Lines • Show Last 20 Lines |
It might be nice to encode this in the g_labels[] table somehow instead.