1219820Sjeff/*-
2219820Sjeff *  Copyright (c) 2005 Chris Jones
3219820Sjeff *  All rights reserved.
4219820Sjeff *
5219820Sjeff * This software was developed for the FreeBSD Project by Chris Jones
6219820Sjeff * thanks to the support of Google's Summer of Code program and
7219820Sjeff * mentoring by Lukas Ertl.
8219820Sjeff *
9219820Sjeff * Redistribution and use in source and binary forms, with or without
10219820Sjeff * modification, are permitted provided that the following conditions
11219820Sjeff * are met:
12219820Sjeff * 1. Redistributions of source code must retain the above copyright
13219820Sjeff *    notice, this list of conditions and the following disclaimer.
14219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright
15219820Sjeff *    notice, this list of conditions and the following disclaimer in the
16219820Sjeff *    documentation and/or other materials provided with the distribution.
17219820Sjeff *
18219820Sjeff * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19219820Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20219820Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21219820Sjeff * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22219820Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23219820Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24219820Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25219820Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26219820Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27219820Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28219820Sjeff * SUCH DAMAGE.
29219820Sjeff *
30219820Sjeff */
31219820Sjeff
32219820Sjeff#include <sys/cdefs.h>
33219820Sjeff__FBSDID("$FreeBSD$");
34219820Sjeff
35219820Sjeff#include <sys/param.h>
36219820Sjeff#include <sys/libkern.h>
37219820Sjeff#include <sys/malloc.h>
38219820Sjeff
39219820Sjeff#include <geom/geom.h>
40219820Sjeff#include <geom/vinum/geom_vinum_var.h>
41219820Sjeff#include <geom/vinum/geom_vinum.h>
42219820Sjeff
43219820Sjeffvoid
44219820Sjeffgv_rename(struct g_geom *gp, struct gctl_req *req)
45219820Sjeff{
46219820Sjeff	struct gv_softc *sc;
47219820Sjeff	struct gv_volume *v;
48219820Sjeff	struct gv_plex *p;
49219820Sjeff	struct gv_sd *s;
50219820Sjeff	struct gv_drive *d;
51219820Sjeff	char *newname, *object, *name;
52219820Sjeff	int *flags, type;
53219820Sjeff
54219820Sjeff	sc = gp->softc;
55219820Sjeff
56219820Sjeff	flags = gctl_get_paraml(req, "flags", sizeof(*flags));
57219820Sjeff	if (flags == NULL) {
58219820Sjeff		gctl_error(req, "no flags given");
59219820Sjeff		return;
60219820Sjeff	}
61219820Sjeff
62219820Sjeff	newname = gctl_get_param(req, "newname", NULL);
63219820Sjeff	if (newname == NULL) {
64219820Sjeff		gctl_error(req, "no new name given");
65219820Sjeff		return;
66219820Sjeff	}
67219820Sjeff
68219820Sjeff	object = gctl_get_param(req, "object", NULL);
69219820Sjeff	if (object == NULL) {
70219820Sjeff		gctl_error(req, "no object given");
71219820Sjeff		return;
72219820Sjeff	}
73219820Sjeff
74219820Sjeff	type = gv_object_type(sc, object);
75219820Sjeff	switch (type) {
76219820Sjeff	case GV_TYPE_VOL:
77219820Sjeff		v = gv_find_vol(sc, object);
78219820Sjeff		if (v == NULL) 	{
79219820Sjeff			gctl_error(req, "unknown volume '%s'", object);
80219820Sjeff			return;
81219820Sjeff		}
82219820Sjeff		name = g_malloc(GV_MAXVOLNAME, M_WAITOK | M_ZERO);
83219820Sjeff		strlcpy(name, newname, GV_MAXVOLNAME);
84219820Sjeff		gv_post_event(sc, GV_EVENT_RENAME_VOL, v, name, *flags, 0);
85219820Sjeff		break;
86219820Sjeff	case GV_TYPE_PLEX:
87219820Sjeff		p = gv_find_plex(sc, object);
88219820Sjeff		if (p == NULL) {
89219820Sjeff			gctl_error(req, "unknown plex '%s'", object);
90219820Sjeff			return;
91219820Sjeff		}
92219820Sjeff		name = g_malloc(GV_MAXPLEXNAME, M_WAITOK | M_ZERO);
93219820Sjeff		strlcpy(name, newname, GV_MAXPLEXNAME);
94219820Sjeff		gv_post_event(sc, GV_EVENT_RENAME_PLEX, p, name, *flags, 0);
95219820Sjeff		break;
96219820Sjeff	case GV_TYPE_SD:
97219820Sjeff		s = gv_find_sd(sc, object);
98219820Sjeff		if (s == NULL) {
99219820Sjeff			gctl_error(req, "unknown subdisk '%s'", object);
100219820Sjeff			return;
101219820Sjeff		}
102219820Sjeff		name = g_malloc(GV_MAXSDNAME, M_WAITOK | M_ZERO);
103219820Sjeff		strlcpy(name, newname, GV_MAXSDNAME);
104219820Sjeff		gv_post_event(sc, GV_EVENT_RENAME_SD, s, name, *flags, 0);
105219820Sjeff		break;
106219820Sjeff	case GV_TYPE_DRIVE:
107219820Sjeff		d = gv_find_drive(sc, object);
108219820Sjeff		if (d == NULL) {
109219820Sjeff			gctl_error(req, "unknown drive '%s'", object);
110219820Sjeff			return;
111219820Sjeff		}
112219820Sjeff		name = g_malloc(GV_MAXDRIVENAME, M_WAITOK | M_ZERO);
113219820Sjeff		strlcpy(name, newname, GV_MAXDRIVENAME);
114219820Sjeff		gv_post_event(sc, GV_EVENT_RENAME_DRIVE, d, name, *flags, 0);
115219820Sjeff		break;
116219820Sjeff	default:
117219820Sjeff		gctl_error(req, "unknown object '%s'", object);
118219820Sjeff		return;
119219820Sjeff	}
120219820Sjeff}
121219820Sjeff
122219820Sjeffint
123219820Sjeffgv_rename_drive(struct gv_softc *sc, struct gv_drive *d, char *newname,
124219820Sjeff    int flags)
125219820Sjeff{
126219820Sjeff	struct gv_sd *s;
127219820Sjeff
128219820Sjeff	KASSERT(d != NULL, ("gv_rename_drive: NULL d"));
129219820Sjeff
130219820Sjeff	if (gv_object_type(sc, newname) != GV_ERR_NOTFOUND) {
131219820Sjeff		G_VINUM_DEBUG(1, "drive name '%s' already in use", newname);
132219820Sjeff		return (GV_ERR_NAMETAKEN);
133219820Sjeff	}
134219820Sjeff
135219820Sjeff	strlcpy(d->name, newname, sizeof(d->name));
136219820Sjeff	if (d->hdr != NULL)
137219820Sjeff		strlcpy(d->hdr->label.name, newname, sizeof(d->hdr->label.name));
138219820Sjeff
139219820Sjeff	LIST_FOREACH(s, &d->subdisks, from_drive)
140219820Sjeff		strlcpy(s->drive, d->name, sizeof(s->drive));
141219820Sjeff
142219820Sjeff	return (0);
143219820Sjeff}
144219820Sjeff
145219820Sjeffint
146219820Sjeffgv_rename_plex(struct gv_softc *sc, struct gv_plex *p, char *newname, int flags)
147219820Sjeff{
148219820Sjeff	char newsd[GV_MAXSDNAME];
149219820Sjeff	struct gv_sd *s;
150219820Sjeff	char *ptr;
151219820Sjeff	int err;
152219820Sjeff
153219820Sjeff	KASSERT(p != NULL, ("gv_rename_plex: NULL p"));
154219820Sjeff
155219820Sjeff	if (gv_object_type(sc, newname) != GV_ERR_NOTFOUND) {
156219820Sjeff		G_VINUM_DEBUG(1, "plex name '%s' already in use", newname);
157219820Sjeff		return (GV_ERR_NAMETAKEN);
158219820Sjeff	}
159219820Sjeff
160219820Sjeff	/*
161219820Sjeff	 * Locate the plex number part of the plex names.
162219820Sjeff	 * XXX: might be a good idea to sanitize input a bit more
163219820Sjeff	 */
164219820Sjeff	ptr = strrchr(newname, '.');
165219820Sjeff	if (ptr == NULL) {
166219820Sjeff		G_VINUM_DEBUG(0, "proposed plex name '%s' is not a valid plex "
167219820Sjeff		    "name", newname);
168219820Sjeff		return (GV_ERR_INVNAME);
169219820Sjeff	}
170219820Sjeff
171219820Sjeff	strlcpy(p->name, newname, sizeof(p->name));
172219820Sjeff
173219820Sjeff	/* Fix up references and potentially rename subdisks. */
174219820Sjeff	LIST_FOREACH(s, &p->subdisks, in_plex) {
175219820Sjeff		strlcpy(s->plex, p->name, sizeof(s->plex));
176219820Sjeff		if (flags & GV_FLAG_R) {
177219820Sjeff			/*
178219820Sjeff			 * Look for the two last dots in the string, and assume
179219820Sjeff			 * that the old value was ok.
180219820Sjeff			 */
181219820Sjeff			ptr = strrchr(s->name, '.');
182219820Sjeff			if (ptr == NULL)
183219820Sjeff				return (GV_ERR_INVNAME);
184219820Sjeff			ptr++;
185219820Sjeff			snprintf(newsd, sizeof(newsd), "%s.%s", p->name, ptr);
186219820Sjeff			err = gv_rename_sd(sc, s, newsd, flags);
187219820Sjeff			if (err)
188219820Sjeff				return (err);
189219820Sjeff		}
190219820Sjeff	}
191219820Sjeff	return (0);
192219820Sjeff}
193219820Sjeff
194219820Sjeff/*
195219820Sjeff * gv_rename_sd: renames a subdisk.  Note that the 'flags' argument is ignored,
196219820Sjeff * since there are no structures below a subdisk.  Similarly, we don't have to
197219820Sjeff * clean up any references elsewhere to the subdisk's name.
198219820Sjeff */
199219820Sjeffint
200219820Sjeffgv_rename_sd(struct gv_softc *sc, struct gv_sd *s, char *newname, int flags)
201219820Sjeff{
202219820Sjeff	char *dot1, *dot2;
203219820Sjeff
204219820Sjeff	KASSERT(s != NULL, ("gv_rename_sd: NULL s"));
205219820Sjeff
206219820Sjeff	if (gv_object_type(sc, newname) != GV_ERR_NOTFOUND) {
207219820Sjeff		G_VINUM_DEBUG(1, "subdisk name %s already in use", newname);
208219820Sjeff		return (GV_ERR_NAMETAKEN);
209219820Sjeff	}
210219820Sjeff
211219820Sjeff	/* Locate the sd number part of the sd names. */
212219820Sjeff	dot1 = strchr(newname, '.');
213219820Sjeff	if (dot1 == NULL || (dot2 = strchr(dot1 +  1, '.')) == NULL) {
214219820Sjeff		G_VINUM_DEBUG(0, "proposed sd name '%s' is not a valid sd name",
215219820Sjeff		    newname);
216219820Sjeff		return (GV_ERR_INVNAME);
217219820Sjeff	}
218219820Sjeff	strlcpy(s->name, newname, sizeof(s->name));
219219820Sjeff	return (0);
220219820Sjeff}
221219820Sjeff
222219820Sjeffint
223219820Sjeffgv_rename_vol(struct gv_softc *sc, struct gv_volume *v, char *newname,
224219820Sjeff    int flags)
225219820Sjeff{
226219820Sjeff	struct g_provider *pp;
227219820Sjeff	struct gv_plex *p;
228219820Sjeff	char newplex[GV_MAXPLEXNAME], *ptr;
229219820Sjeff	int err;
230219820Sjeff
231219820Sjeff	KASSERT(v != NULL, ("gv_rename_vol: NULL v"));
232219820Sjeff	pp = v->provider;
233219820Sjeff	KASSERT(pp != NULL, ("gv_rename_vol: NULL pp"));
234219820Sjeff
235219820Sjeff	if (gv_object_type(sc, newname) != GV_ERR_NOTFOUND) {
236219820Sjeff		G_VINUM_DEBUG(1, "volume name %s already in use", newname);
237219820Sjeff		return (GV_ERR_NAMETAKEN);
238219820Sjeff	}
239219820Sjeff
240219820Sjeff	/* Rename the volume. */
241219820Sjeff	strlcpy(v->name, newname, sizeof(v->name));
242219820Sjeff
243219820Sjeff	/* Fix up references and potentially rename plexes. */
244219820Sjeff	LIST_FOREACH(p, &v->plexes, in_volume) {
245219820Sjeff		strlcpy(p->volume, v->name, sizeof(p->volume));
246219820Sjeff		if (flags & GV_FLAG_R) {
247219820Sjeff			/*
248219820Sjeff			 * Look for the last dot in the string, and assume that
249219820Sjeff			 * the old value was ok.
250219820Sjeff			 */
251219820Sjeff			ptr = strrchr(p->name, '.');
252219820Sjeff			ptr++;
253219820Sjeff			snprintf(newplex, sizeof(newplex), "%s.%s", v->name, ptr);
254219820Sjeff			err = gv_rename_plex(sc, p, newplex, flags);
255219820Sjeff			if (err)
256219820Sjeff				return (err);
257219820Sjeff		}
258219820Sjeff	}
259219820Sjeff
260219820Sjeff	return (0);
261219820Sjeff}
262219820Sjeff