Path: blob/main/sys/contrib/openzfs/module/zcommon/zfs_namecheck.c
48383 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* 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.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/21/*22* Copyright 2009 Sun Microsystems, Inc. All rights reserved.23* Use is subject to license terms.24*/25/*26* Copyright (c) 2013, 2016 by Delphix. All rights reserved.27*/2829/*30* Common name validation routines for ZFS. These routines are shared by the31* userland code as well as the ioctl() layer to ensure that we don't32* inadvertently expose a hole through direct ioctl()s that never gets tested.33* In userland, however, we want significantly more information about _why_ the34* 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 why36* the name failed to validate.37*/3839#if !defined(_KERNEL)40#include <string.h>41#endif4243#include <sys/dsl_dir.h>44#include <sys/param.h>45#include <sys/nvpair.h>46#include "zfs_namecheck.h"47#include "zfs_deleg.h"4849/*50* Deeply nested datasets can overflow the stack, so we put a limit51* in the amount of nesting a path can have. zfs_max_dataset_nesting52* can be tuned temporarily to fix existing datasets that exceed our53* predefined limit.54*/55int zfs_max_dataset_nesting = 50;5657static int58valid_char(char c)59{60return ((c >= 'a' && c <= 'z') ||61(c >= 'A' && c <= 'Z') ||62(c >= '0' && c <= '9') ||63c == '-' || c == '_' || c == '.' || c == ':' || c == ' ');64}6566/*67* Looks at a path and returns its level of nesting (depth).68*/69int70get_dataset_depth(const char *path)71{72const char *loc = path;73int nesting = 0;7475/*76* Keep track of nesting until you hit the end of the77* path or found the snapshot/bookmark separator.78*/79for (int i = 0; loc[i] != '\0' &&80loc[i] != '@' &&81loc[i] != '#'; i++) {82if (loc[i] == '/')83nesting++;84}8586return (nesting);87}8889/*90* Snapshot names must be made up of alphanumeric characters plus the following91* characters:92*93* [-_.: ]94*95* Returns 0 on success, -1 on error.96*/97int98zfs_component_namecheck(const char *path, namecheck_err_t *why, char *what)99{100const char *loc;101102if (strlen(path) >= ZFS_MAX_DATASET_NAME_LEN) {103if (why)104*why = NAME_ERR_TOOLONG;105return (-1);106}107108if (path[0] == '\0') {109if (why)110*why = NAME_ERR_EMPTY_COMPONENT;111return (-1);112}113114for (loc = path; *loc; loc++) {115if (!valid_char(*loc)) {116if (why) {117*why = NAME_ERR_INVALCHAR;118*what = *loc;119}120return (-1);121}122}123return (0);124}125126127/*128* Permissions set name must start with the letter '@' followed by the129* same character restrictions as snapshot names, except that the name130* cannot exceed 64 characters.131*132* Returns 0 on success, -1 on error.133*/134int135permset_namecheck(const char *path, namecheck_err_t *why, char *what)136{137if (strlen(path) >= ZFS_PERMSET_MAXLEN) {138if (why)139*why = NAME_ERR_TOOLONG;140return (-1);141}142143if (path[0] != '@') {144if (why) {145*why = NAME_ERR_NO_AT;146*what = path[0];147}148return (-1);149}150151return (zfs_component_namecheck(&path[1], why, what));152}153154/*155* Dataset paths should not be deeper than zfs_max_dataset_nesting156* in terms of nesting.157*158* Returns 0 on success, -1 on error.159*/160int161dataset_nestcheck(const char *path)162{163return ((get_dataset_depth(path) < zfs_max_dataset_nesting) ? 0 : -1);164}165166/*167* Entity names must be of the following form:168*169* [component/]*[component][(@|#)component]?170*171* Where each component is made up of alphanumeric characters plus the following172* characters:173*174* [-_.: %]175*176* We allow '%' here as we use that character internally to create unique177* names for temporary clones (for online recv).178*179* Returns 0 on success, -1 on error.180*/181int182entity_namecheck(const char *path, namecheck_err_t *why, char *what)183{184const char *end;185186EQUIV(why == NULL, what == NULL);187188/*189* Make sure the name is not too long.190*/191if (strlen(path) >= ZFS_MAX_DATASET_NAME_LEN) {192if (why)193*why = NAME_ERR_TOOLONG;194return (-1);195}196197/* Explicitly check for a leading slash. */198if (path[0] == '/') {199if (why)200*why = NAME_ERR_LEADING_SLASH;201return (-1);202}203204if (path[0] == '\0') {205if (why)206*why = NAME_ERR_EMPTY_COMPONENT;207return (-1);208}209210const char *start = path;211boolean_t found_delim = B_FALSE;212for (;;) {213/* Find the end of this component */214end = start;215while (*end != '/' && *end != '@' && *end != '#' &&216*end != '\0')217end++;218219if (*end == '\0' && end[-1] == '/') {220/* trailing slashes are not allowed */221if (why)222*why = NAME_ERR_TRAILING_SLASH;223return (-1);224}225226/* Validate the contents of this component */227for (const char *loc = start; loc != end; loc++) {228if (!valid_char(*loc) && *loc != '%') {229if (why) {230*why = NAME_ERR_INVALCHAR;231*what = *loc;232}233return (-1);234}235}236237if (*end == '\0' || *end == '/') {238int component_length = end - start;239/* Validate the contents of this component is not '.' */240if (component_length == 1) {241if (start[0] == '.') {242if (why)243*why = NAME_ERR_SELF_REF;244return (-1);245}246}247248/* Validate the content of this component is not '..' */249if (component_length == 2) {250if (start[0] == '.' && start[1] == '.') {251if (why)252*why = NAME_ERR_PARENT_REF;253return (-1);254}255}256}257258/* Snapshot or bookmark delimiter found */259if (*end == '@' || *end == '#') {260/* Multiple delimiters are not allowed */261if (found_delim != 0) {262if (why)263*why = NAME_ERR_MULTIPLE_DELIMITERS;264return (-1);265}266267found_delim = B_TRUE;268}269270/* Zero-length components are not allowed */271if (start == end) {272if (why)273*why = NAME_ERR_EMPTY_COMPONENT;274return (-1);275}276277/* If we've reached the end of the string, we're OK */278if (*end == '\0')279return (0);280281/*282* If there is a '/' in a snapshot or bookmark name283* then report an error284*/285if (*end == '/' && found_delim != 0) {286if (why)287*why = NAME_ERR_TRAILING_SLASH;288return (-1);289}290291/* Update to the next component */292start = end + 1;293}294}295296/*297* Dataset is any entity, except bookmark298*/299int300dataset_namecheck(const char *path, namecheck_err_t *why, char *what)301{302int ret = entity_namecheck(path, why, what);303304if (ret == 0 && strchr(path, '#') != NULL) {305if (why != NULL) {306*why = NAME_ERR_INVALCHAR;307*what = '#';308}309return (-1);310}311312return (ret);313}314315/*316* Assert path is a valid bookmark name317*/318int319bookmark_namecheck(const char *path, namecheck_err_t *why, char *what)320{321int ret = entity_namecheck(path, why, what);322323if (ret == 0 && strchr(path, '#') == NULL) {324if (why != NULL) {325*why = NAME_ERR_NO_POUND;326*what = '#';327}328return (-1);329}330331return (ret);332}333334/*335* Assert path is a valid snapshot name336*/337int338snapshot_namecheck(const char *path, namecheck_err_t *why, char *what)339{340int ret = entity_namecheck(path, why, what);341342if (ret == 0 && strchr(path, '@') == NULL) {343if (why != NULL) {344*why = NAME_ERR_NO_AT;345*what = '@';346}347return (-1);348}349350return (ret);351}352353/*354* mountpoint names must be of the following form:355*356* /[component][/]*[component][/]357*358* Returns 0 on success, -1 on error.359*/360int361mountpoint_namecheck(const char *path, namecheck_err_t *why)362{363const char *start, *end;364365/*366* Make sure none of the mountpoint component names are too long.367* If a component name is too long then the mkdir of the mountpoint368* will fail but then the mountpoint property will be set to a value369* that can never be mounted. Better to fail before setting the prop.370* Extra slashes are OK, they will be tossed by the mountpoint mkdir.371*/372373if (path == NULL || *path != '/') {374if (why)375*why = NAME_ERR_LEADING_SLASH;376return (-1);377}378379/* Skip leading slash */380start = &path[1];381do {382end = start;383while (*end != '/' && *end != '\0')384end++;385386if (end - start >= ZFS_MAX_DATASET_NAME_LEN) {387if (why)388*why = NAME_ERR_TOOLONG;389return (-1);390}391start = end + 1;392393} while (*end != '\0');394395return (0);396}397398/*399* For pool names, we have the same set of valid characters as described in400* dataset names, with the additional restriction that the pool name must begin401* with a letter. The pool names 'raidz' and 'mirror' are also reserved names402* that cannot be used.403*404* Returns 0 on success, -1 on error.405*/406int407pool_namecheck(const char *pool, namecheck_err_t *why, char *what)408{409const char *c;410411/*412* Make sure the name is not too long.413* If we're creating a pool with version >= SPA_VERSION_DSL_SCRUB (v11)414* we need to account for additional space needed by the origin ds which415* will also be snapshotted: "poolname"+"/"+"$ORIGIN"+"@"+"$ORIGIN".416* Play it safe and enforce this limit even if the pool version is < 11417* so it can be upgraded without issues.418*/419if (strlen(pool) >= (ZFS_MAX_DATASET_NAME_LEN - 2 -420strlen(ORIGIN_DIR_NAME) * 2)) {421if (why)422*why = NAME_ERR_TOOLONG;423return (-1);424}425426c = pool;427while (*c != '\0') {428if (!valid_char(*c)) {429if (why) {430*why = NAME_ERR_INVALCHAR;431*what = *c;432}433return (-1);434}435c++;436}437438if (!(*pool >= 'a' && *pool <= 'z') &&439!(*pool >= 'A' && *pool <= 'Z')) {440if (why)441*why = NAME_ERR_NOLETTER;442return (-1);443}444445if (strcmp(pool, "mirror") == 0 ||446strcmp(pool, "raidz") == 0 ||447strcmp(pool, "draid") == 0) {448if (why)449*why = NAME_ERR_RESERVED;450return (-1);451}452453return (0);454}455456EXPORT_SYMBOL(entity_namecheck);457EXPORT_SYMBOL(pool_namecheck);458EXPORT_SYMBOL(dataset_namecheck);459EXPORT_SYMBOL(bookmark_namecheck);460EXPORT_SYMBOL(snapshot_namecheck);461EXPORT_SYMBOL(zfs_component_namecheck);462EXPORT_SYMBOL(dataset_nestcheck);463EXPORT_SYMBOL(get_dataset_depth);464EXPORT_SYMBOL(zfs_max_dataset_nesting);465466ZFS_MODULE_PARAM(zfs, zfs_, max_dataset_nesting, INT, ZMOD_RW,467"Limit to the amount of nesting a path can have. Defaults to 50.");468469470