cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nfsroot.c (9947B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 *  Copyright (C) 1995, 1996  Gero Kuhlmann <gero@gkminix.han.de>
      4 *
      5 *  Allow an NFS filesystem to be mounted as root. The way this works is:
      6 *     (1) Use the IP autoconfig mechanism to set local IP addresses and routes.
      7 *     (2) Construct the device string and the options string using DHCP
      8 *         option 17 and/or kernel command line options.
      9 *     (3) When mount_root() sets up the root file system, pass these strings
     10 *         to the NFS client's regular mount interface via sys_mount().
     11 *
     12 *
     13 *	Changes:
     14 *
     15 *	Alan Cox	:	Removed get_address name clash with FPU.
     16 *	Alan Cox	:	Reformatted a bit.
     17 *	Gero Kuhlmann	:	Code cleanup
     18 *	Michael Rausch  :	Fixed recognition of an incoming RARP answer.
     19 *	Martin Mares	: (2.0)	Auto-configuration via BOOTP supported.
     20 *	Martin Mares	:	Manual selection of interface & BOOTP/RARP.
     21 *	Martin Mares	:	Using network routes instead of host routes,
     22 *				allowing the default configuration to be used
     23 *				for normal operation of the host.
     24 *	Martin Mares	:	Randomized timer with exponential backoff
     25 *				installed to minimize network congestion.
     26 *	Martin Mares	:	Code cleanup.
     27 *	Martin Mares	: (2.1)	BOOTP and RARP made configuration options.
     28 *	Martin Mares	:	Server hostname generation fixed.
     29 *	Gerd Knorr	:	Fixed wired inode handling
     30 *	Martin Mares	: (2.2)	"0.0.0.0" addresses from command line ignored.
     31 *	Martin Mares	:	RARP replies not tested for server address.
     32 *	Gero Kuhlmann	: (2.3) Some bug fixes and code cleanup again (please
     33 *				send me your new patches _before_ bothering
     34 *				Linus so that I don' always have to cleanup
     35 *				_afterwards_ - thanks)
     36 *	Gero Kuhlmann	:	Last changes of Martin Mares undone.
     37 *	Gero Kuhlmann	: 	RARP replies are tested for specified server
     38 *				again. However, it's now possible to have
     39 *				different RARP and NFS servers.
     40 *	Gero Kuhlmann	:	"0.0.0.0" addresses from command line are
     41 *				now mapped to INADDR_NONE.
     42 *	Gero Kuhlmann	:	Fixed a bug which prevented BOOTP path name
     43 *				from being used (thanks to Leo Spiekman)
     44 *	Andy Walker	:	Allow to specify the NFS server in nfs_root
     45 *				without giving a path name
     46 *	Swen Thümmler	:	Allow to specify the NFS options in nfs_root
     47 *				without giving a path name. Fix BOOTP request
     48 *				for domainname (domainname is NIS domain, not
     49 *				DNS domain!). Skip dummy devices for BOOTP.
     50 *	Jacek Zapala	:	Fixed a bug which prevented server-ip address
     51 *				from nfsroot parameter from being used.
     52 *	Olaf Kirch	:	Adapted to new NFS code.
     53 *	Jakub Jelinek	:	Free used code segment.
     54 *	Marko Kohtala	:	Fixed some bugs.
     55 *	Martin Mares	:	Debug message cleanup
     56 *	Martin Mares	:	Changed to use the new generic IP layer autoconfig
     57 *				code. BOOTP and RARP moved there.
     58 *	Martin Mares	:	Default path now contains host name instead of
     59 *				host IP address (but host name defaults to IP
     60 *				address anyway).
     61 *	Martin Mares	:	Use root_server_addr appropriately during setup.
     62 *	Martin Mares	:	Rewrote parameter parsing, now hopefully giving
     63 *				correct overriding.
     64 *	Trond Myklebust :	Add in preliminary support for NFSv3 and TCP.
     65 *				Fix bug in root_nfs_addr(). nfs_data.namlen
     66 *				is NOT for the length of the hostname.
     67 *	Hua Qin		:	Support for mounting root file system via
     68 *				NFS over TCP.
     69 *	Fabian Frederick:	Option parser rebuilt (using parser lib)
     70 *	Chuck Lever	:	Use super.c's text-based mount option parsing
     71 *	Chuck Lever	:	Add "nfsrootdebug".
     72 */
     73
     74#include <linux/types.h>
     75#include <linux/string.h>
     76#include <linux/init.h>
     77#include <linux/nfs.h>
     78#include <linux/nfs_fs.h>
     79#include <linux/utsname.h>
     80#include <linux/root_dev.h>
     81#include <net/ipconfig.h>
     82
     83#include "internal.h"
     84
     85#define NFSDBG_FACILITY NFSDBG_ROOT
     86
     87/* Default path we try to mount. "%s" gets replaced by our IP address */
     88#define NFS_ROOT		"/tftpboot/%s"
     89
     90/* Default NFSROOT mount options. */
     91#if defined(CONFIG_NFS_V2)
     92#define NFS_DEF_OPTIONS		"vers=2,tcp,rsize=4096,wsize=4096"
     93#elif defined(CONFIG_NFS_V3)
     94#define NFS_DEF_OPTIONS		"vers=3,tcp,rsize=4096,wsize=4096"
     95#else
     96#define NFS_DEF_OPTIONS		"vers=4,tcp,rsize=4096,wsize=4096"
     97#endif
     98
     99/* Parameters passed from the kernel command line */
    100static char nfs_root_parms[NFS_MAXPATHLEN + 1] __initdata = "";
    101
    102/* Text-based mount options passed to super.c */
    103static char nfs_root_options[256] __initdata = NFS_DEF_OPTIONS;
    104
    105/* Address of NFS server */
    106static __be32 servaddr __initdata = htonl(INADDR_NONE);
    107
    108/* Name of directory to mount */
    109static char nfs_export_path[NFS_MAXPATHLEN + 1] __initdata = "";
    110
    111/* server:export path string passed to super.c */
    112static char nfs_root_device[NFS_MAXPATHLEN + 1] __initdata = "";
    113
    114#ifdef NFS_DEBUG
    115/*
    116 * When the "nfsrootdebug" kernel command line option is specified,
    117 * enable debugging messages for NFSROOT.
    118 */
    119static int __init nfs_root_debug(char *__unused)
    120{
    121	nfs_debug |= NFSDBG_ROOT | NFSDBG_MOUNT;
    122	return 1;
    123}
    124
    125__setup("nfsrootdebug", nfs_root_debug);
    126#endif
    127
    128/*
    129 *  Parse NFS server and directory information passed on the kernel
    130 *  command line.
    131 *
    132 *  nfsroot=[<server-ip>:]<root-dir>[,<nfs-options>]
    133 *
    134 *  If there is a "%s" token in the <root-dir> string, it is replaced
    135 *  by the ASCII-representation of the client's IP address.
    136 */
    137static int __init nfs_root_setup(char *line)
    138{
    139	ROOT_DEV = Root_NFS;
    140
    141	if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) {
    142		strlcpy(nfs_root_parms, line, sizeof(nfs_root_parms));
    143	} else {
    144		size_t n = strlen(line) + sizeof(NFS_ROOT) - 1;
    145		if (n >= sizeof(nfs_root_parms))
    146			line[sizeof(nfs_root_parms) - sizeof(NFS_ROOT) - 2] = '\0';
    147		sprintf(nfs_root_parms, NFS_ROOT, line);
    148	}
    149
    150	/*
    151	 * Extract the IP address of the NFS server containing our
    152	 * root file system, if one was specified.
    153	 *
    154	 * Note: root_nfs_parse_addr() removes the server-ip from
    155	 *	 nfs_root_parms, if it exists.
    156	 */
    157	root_server_addr = root_nfs_parse_addr(nfs_root_parms);
    158
    159	return 1;
    160}
    161
    162__setup("nfsroot=", nfs_root_setup);
    163
    164static int __init root_nfs_copy(char *dest, const char *src,
    165				     const size_t destlen)
    166{
    167	if (strlcpy(dest, src, destlen) > destlen)
    168		return -1;
    169	return 0;
    170}
    171
    172static int __init root_nfs_cat(char *dest, const char *src,
    173			       const size_t destlen)
    174{
    175	size_t len = strlen(dest);
    176
    177	if (len && dest[len - 1] != ',')
    178		if (strlcat(dest, ",", destlen) > destlen)
    179			return -1;
    180
    181	if (strlcat(dest, src, destlen) > destlen)
    182		return -1;
    183	return 0;
    184}
    185
    186/*
    187 * Parse out root export path and mount options from
    188 * passed-in string @incoming.
    189 *
    190 * Copy the export path into @exppath.
    191 */
    192static int __init root_nfs_parse_options(char *incoming, char *exppath,
    193					 const size_t exppathlen)
    194{
    195	char *p;
    196
    197	/*
    198	 * Set the NFS remote path
    199	 */
    200	p = strsep(&incoming, ",");
    201	if (*p != '\0' && strcmp(p, "default") != 0)
    202		if (root_nfs_copy(exppath, p, exppathlen))
    203			return -1;
    204
    205	/*
    206	 * @incoming now points to the rest of the string; if it
    207	 * contains something, append it to our root options buffer
    208	 */
    209	if (incoming != NULL && *incoming != '\0')
    210		if (root_nfs_cat(nfs_root_options, incoming,
    211						sizeof(nfs_root_options)))
    212			return -1;
    213	return 0;
    214}
    215
    216/*
    217 *  Decode the export directory path name and NFS options from
    218 *  the kernel command line.  This has to be done late in order to
    219 *  use a dynamically acquired client IP address for the remote
    220 *  root directory path.
    221 *
    222 *  Returns zero if successful; otherwise -1 is returned.
    223 */
    224static int __init root_nfs_data(char *cmdline)
    225{
    226	char mand_options[sizeof("nolock,addr=") + INET_ADDRSTRLEN + 1];
    227	int len, retval = -1;
    228	char *tmp = NULL;
    229	const size_t tmplen = sizeof(nfs_export_path);
    230
    231	tmp = kzalloc(tmplen, GFP_KERNEL);
    232	if (tmp == NULL)
    233		goto out_nomem;
    234	strcpy(tmp, NFS_ROOT);
    235
    236	if (root_server_path[0] != '\0') {
    237		dprintk("Root-NFS: DHCPv4 option 17: %s\n",
    238			root_server_path);
    239		if (root_nfs_parse_options(root_server_path, tmp, tmplen))
    240			goto out_optionstoolong;
    241	}
    242
    243	if (cmdline[0] != '\0') {
    244		dprintk("Root-NFS: nfsroot=%s\n", cmdline);
    245		if (root_nfs_parse_options(cmdline, tmp, tmplen))
    246			goto out_optionstoolong;
    247	}
    248
    249	/*
    250	 * Append mandatory options for nfsroot so they override
    251	 * what has come before
    252	 */
    253	snprintf(mand_options, sizeof(mand_options), "nolock,addr=%pI4",
    254			&servaddr);
    255	if (root_nfs_cat(nfs_root_options, mand_options,
    256						sizeof(nfs_root_options)))
    257		goto out_optionstoolong;
    258
    259	/*
    260	 * Set up nfs_root_device.  For NFS mounts, this looks like
    261	 *
    262	 *	server:/path
    263	 *
    264	 * At this point, utsname()->nodename contains our local
    265	 * IP address or hostname, set by ipconfig.  If "%s" exists
    266	 * in tmp, substitute the nodename, then shovel the whole
    267	 * mess into nfs_root_device.
    268	 */
    269	len = snprintf(nfs_export_path, sizeof(nfs_export_path),
    270				tmp, utsname()->nodename);
    271	if (len >= (int)sizeof(nfs_export_path))
    272		goto out_devnametoolong;
    273	len = snprintf(nfs_root_device, sizeof(nfs_root_device),
    274				"%pI4:%s", &servaddr, nfs_export_path);
    275	if (len >= (int)sizeof(nfs_root_device))
    276		goto out_devnametoolong;
    277
    278	retval = 0;
    279
    280out:
    281	kfree(tmp);
    282	return retval;
    283out_nomem:
    284	printk(KERN_ERR "Root-NFS: could not allocate memory\n");
    285	goto out;
    286out_optionstoolong:
    287	printk(KERN_ERR "Root-NFS: mount options string too long\n");
    288	goto out;
    289out_devnametoolong:
    290	printk(KERN_ERR "Root-NFS: root device name too long.\n");
    291	goto out;
    292}
    293
    294/**
    295 * nfs_root_data - Return prepared 'data' for NFSROOT mount
    296 * @root_device: OUT: address of string containing NFSROOT device
    297 * @root_data: OUT: address of string containing NFSROOT mount options
    298 *
    299 * Returns zero and sets @root_device and @root_data if successful,
    300 * otherwise -1 is returned.
    301 */
    302int __init nfs_root_data(char **root_device, char **root_data)
    303{
    304	servaddr = root_server_addr;
    305	if (servaddr == htonl(INADDR_NONE)) {
    306		printk(KERN_ERR "Root-NFS: no NFS server address\n");
    307		return -1;
    308	}
    309
    310	if (root_nfs_data(nfs_root_parms) < 0)
    311		return -1;
    312
    313	*root_device = nfs_root_device;
    314	*root_data = nfs_root_options;
    315	return 0;
    316}