zfs_namecheck.c revision 307122
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25/*
26 * Copyright (c) 2013 by Delphix. All rights reserved.
27 */
28
29/*
30 * Common name validation routines for ZFS.  These routines are shared by the
31 * userland code as well as the ioctl() layer to ensure that we don't
32 * inadvertently expose a hole through direct ioctl()s that never gets tested.
33 * In userland, however, we want significantly more information about _why_ the
34 * name is invalid.  In the kernel, we only care whether it's valid or not.
35 * Each routine therefore takes a 'namecheck_err_t' which describes exactly why
36 * the name failed to validate.
37 *
38 * Each function returns 0 on success, -1 on error.
39 */
40
41#if defined(_KERNEL)
42#include <sys/systm.h>
43#else
44#include <string.h>
45#endif
46
47#include <sys/param.h>
48#include <sys/nvpair.h>
49#include "zfs_namecheck.h"
50#include "zfs_deleg.h"
51
52static int
53valid_char(char c)
54{
55	return ((c >= 'a' && c <= 'z') ||
56	    (c >= 'A' && c <= 'Z') ||
57	    (c >= '0' && c <= '9') ||
58	    c == '-' || c == '_' || c == '.' || c == ':' || c == ' ');
59}
60
61/*
62 * Snapshot names must be made up of alphanumeric characters plus the following
63 * characters:
64 *
65 * 	[-_.: ]
66 */
67int
68zfs_component_namecheck(const char *path, namecheck_err_t *why, char *what)
69{
70	const char *loc;
71
72	if (strlen(path) >= ZFS_MAX_DATASET_NAME_LEN) {
73		if (why)
74			*why = NAME_ERR_TOOLONG;
75		return (-1);
76	}
77
78	if (path[0] == '\0') {
79		if (why)
80			*why = NAME_ERR_EMPTY_COMPONENT;
81		return (-1);
82	}
83
84	for (loc = path; *loc; loc++) {
85		if (!valid_char(*loc)) {
86			if (why) {
87				*why = NAME_ERR_INVALCHAR;
88				*what = *loc;
89			}
90			return (-1);
91		}
92	}
93	return (0);
94}
95
96
97/*
98 * Permissions set name must start with the letter '@' followed by the
99 * same character restrictions as snapshot names, except that the name
100 * cannot exceed 64 characters.
101 */
102int
103permset_namecheck(const char *path, namecheck_err_t *why, char *what)
104{
105	if (strlen(path) >= ZFS_PERMSET_MAXLEN) {
106		if (why)
107			*why = NAME_ERR_TOOLONG;
108		return (-1);
109	}
110
111	if (path[0] != '@') {
112		if (why) {
113			*why = NAME_ERR_NO_AT;
114			*what = path[0];
115		}
116		return (-1);
117	}
118
119	return (zfs_component_namecheck(&path[1], why, what));
120}
121
122/*
123 * Dataset names must be of the following form:
124 *
125 * 	[component][/]*[component][@component]
126 *
127 * Where each component is made up of alphanumeric characters plus the following
128 * characters:
129 *
130 * 	[-_.:%]
131 *
132 * We allow '%' here as we use that character internally to create unique
133 * names for temporary clones (for online recv).
134 */
135int
136dataset_namecheck(const char *path, namecheck_err_t *why, char *what)
137{
138	const char *loc, *end;
139	int found_snapshot;
140
141	/*
142	 * Make sure the name is not too long.
143	 */
144
145	if (strlen(path) >= ZFS_MAX_DATASET_NAME_LEN) {
146		if (why)
147			*why = NAME_ERR_TOOLONG;
148		return (-1);
149	}
150
151	/* Explicitly check for a leading slash.  */
152	if (path[0] == '/') {
153		if (why)
154			*why = NAME_ERR_LEADING_SLASH;
155		return (-1);
156	}
157
158	if (path[0] == '\0') {
159		if (why)
160			*why = NAME_ERR_EMPTY_COMPONENT;
161		return (-1);
162	}
163
164	loc = path;
165	found_snapshot = 0;
166	for (;;) {
167		/* Find the end of this component */
168		end = loc;
169		while (*end != '/' && *end != '@' && *end != '\0')
170			end++;
171
172		if (*end == '\0' && end[-1] == '/') {
173			/* trailing slashes are not allowed */
174			if (why)
175				*why = NAME_ERR_TRAILING_SLASH;
176			return (-1);
177		}
178
179		/* Zero-length components are not allowed */
180		if (loc == end) {
181			if (why) {
182				/*
183				 * Make sure this is really a zero-length
184				 * component and not a '@@'.
185				 */
186				if (*end == '@' && found_snapshot) {
187					*why = NAME_ERR_MULTIPLE_AT;
188				} else {
189					*why = NAME_ERR_EMPTY_COMPONENT;
190				}
191			}
192
193			return (-1);
194		}
195
196		/* Validate the contents of this component */
197		while (loc != end) {
198			if (!valid_char(*loc) && *loc != '%') {
199				if (why) {
200					*why = NAME_ERR_INVALCHAR;
201					*what = *loc;
202				}
203				return (-1);
204			}
205			loc++;
206		}
207
208		/* If we've reached the end of the string, we're OK */
209		if (*end == '\0')
210			return (0);
211
212		if (*end == '@') {
213			/*
214			 * If we've found an @ symbol, indicate that we're in
215			 * the snapshot component, and report a second '@'
216			 * character as an error.
217			 */
218			if (found_snapshot) {
219				if (why)
220					*why = NAME_ERR_MULTIPLE_AT;
221				return (-1);
222			}
223
224			found_snapshot = 1;
225		}
226
227		/*
228		 * If there is a '/' in a snapshot name
229		 * then report an error
230		 */
231		if (*end == '/' && found_snapshot) {
232			if (why)
233				*why = NAME_ERR_TRAILING_SLASH;
234			return (-1);
235		}
236
237		/* Update to the next component */
238		loc = end + 1;
239	}
240}
241
242
243/*
244 * mountpoint names must be of the following form:
245 *
246 *	/[component][/]*[component][/]
247 */
248int
249mountpoint_namecheck(const char *path, namecheck_err_t *why)
250{
251	const char *start, *end;
252
253	/*
254	 * Make sure none of the mountpoint component names are too long.
255	 * If a component name is too long then the mkdir of the mountpoint
256	 * will fail but then the mountpoint property will be set to a value
257	 * that can never be mounted.  Better to fail before setting the prop.
258	 * Extra slashes are OK, they will be tossed by the mountpoint mkdir.
259	 */
260
261	if (path == NULL || *path != '/') {
262		if (why)
263			*why = NAME_ERR_LEADING_SLASH;
264		return (-1);
265	}
266
267	/* Skip leading slash  */
268	start = &path[1];
269	do {
270		end = start;
271		while (*end != '/' && *end != '\0')
272			end++;
273
274		if (end - start >= ZFS_MAX_DATASET_NAME_LEN) {
275			if (why)
276				*why = NAME_ERR_TOOLONG;
277			return (-1);
278		}
279		start = end + 1;
280
281	} while (*end != '\0');
282
283	return (0);
284}
285
286/*
287 * For pool names, we have the same set of valid characters as described in
288 * dataset names, with the additional restriction that the pool name must begin
289 * with a letter.  The pool names 'raidz' and 'mirror' are also reserved names
290 * that cannot be used.
291 */
292int
293pool_namecheck(const char *pool, namecheck_err_t *why, char *what)
294{
295	const char *c;
296
297	/*
298	 * Make sure the name is not too long.
299	 */
300	if (strlen(pool) >= ZFS_MAX_DATASET_NAME_LEN) {
301		if (why)
302			*why = NAME_ERR_TOOLONG;
303		return (-1);
304	}
305
306	c = pool;
307	while (*c != '\0') {
308		if (!valid_char(*c)) {
309			if (why) {
310				*why = NAME_ERR_INVALCHAR;
311				*what = *c;
312			}
313			return (-1);
314		}
315		c++;
316	}
317
318	if (!(*pool >= 'a' && *pool <= 'z') &&
319	    !(*pool >= 'A' && *pool <= 'Z')) {
320		if (why)
321			*why = NAME_ERR_NOLETTER;
322		return (-1);
323	}
324
325	if (strcmp(pool, "mirror") == 0 || strcmp(pool, "raidz") == 0) {
326		if (why)
327			*why = NAME_ERR_RESERVED;
328		return (-1);
329	}
330
331	if (pool[0] == 'c' && (pool[1] >= '0' && pool[1] <= '9')) {
332		if (why)
333			*why = NAME_ERR_DISKLIKE;
334		return (-1);
335	}
336
337	return (0);
338}
339