Skip to content

Commit

Permalink
cifs: Add support for root file systems
Browse files Browse the repository at this point in the history
Introduce a new CONFIG_CIFS_ROOT option to handle root file systems
over a SMB share.

In order to mount the root file system during the init process, make
cifs.ko perform non-blocking socket operations while mounting and
accessing it.

Cc: Steve French <[email protected]>
Reviewed-by: Aurelien Aptel <[email protected]>
Signed-off-by: Paulo Alcantara (SUSE) <[email protected]>
Signed-off-by: Steve French <[email protected]>
  • Loading branch information
Paulo Alcantara (SUSE) authored and Steve French committed Sep 16, 2019
1 parent 0892ba6 commit 8eecd1c
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 3 deletions.
97 changes: 97 additions & 0 deletions Documentation/filesystems/cifs/cifsroot.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
Mounting root file system via SMB (cifs.ko)
===========================================

Written 2019 by Paulo Alcantara <[email protected]>
Written 2019 by Aurelien Aptel <[email protected]>

The CONFIG_CIFS_ROOT option enables experimental root file system
support over the SMB protocol via cifs.ko.

It introduces a new kernel command-line option called 'cifsroot='
which will tell the kernel to mount the root file system over the
network by utilizing SMB or CIFS protocol.

In order to mount, the network stack will also need to be set up by
using 'ip=' config option. For more details, see
Documentation/filesystems/nfs/nfsroot.txt.

A CIFS root mount currently requires the use of SMB1+UNIX Extensions
which is only supported by the Samba server. SMB1 is the older
deprecated version of the protocol but it has been extended to support
POSIX features (See [1]). The equivalent extensions for the newer
recommended version of the protocol (SMB3) have not been fully
implemented yet which means SMB3 doesn't support some required POSIX
file system objects (e.g. block devices, pipes, sockets).

As a result, a CIFS root will default to SMB1 for now but the version
to use can nonetheless be changed via the 'vers=' mount option. This
default will change once the SMB3 POSIX extensions are fully
implemented.

Server configuration
====================

To enable SMB1+UNIX extensions you will need to set these global
settings in Samba smb.conf:

[global]
server min protocol = NT1
unix extension = yes # default

Kernel command line
===================

root=/dev/cifs

This is just a virtual device that basically tells the kernel to mount
the root file system via SMB protocol.

cifsroot=//<server-ip>/<share>[,options]

Enables the kernel to mount the root file system via SMB that are
located in the <server-ip> and <share> specified in this option.

The default mount options are set in fs/cifs/cifsroot.c.

server-ip
IPv4 address of the server.

share
Path to SMB share (rootfs).

options
Optional mount options. For more information, see mount.cifs(8).

Examples
========

Export root file system as a Samba share in smb.conf file.

...
[linux]
path = /path/to/rootfs
read only = no
guest ok = yes
force user = root
force group = root
browseable = yes
writeable = yes
admin users = root
public = yes
create mask = 0777
directory mask = 0777
...

Restart smb service.

# systemctl restart smb

Test it under QEMU on a kernel built with CONFIG_CIFS_ROOT and
CONFIG_IP_PNP options enabled.

# qemu-system-x86_64 -enable-kvm -cpu host -m 1024 \
-kernel /path/to/linux/arch/x86/boot/bzImage -nographic \
-append "root=/dev/cifs rw ip=dhcp cifsroot=//10.0.2.2/linux,username=foo,password=bar console=ttyS0 3"


1: https://wiki.samba.org/index.php/UNIX_Extensions
8 changes: 8 additions & 0 deletions fs/cifs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,11 @@ config CIFS_FSCACHE
Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data
to be cached locally on disk through the general filesystem cache
manager. If unsure, say N.

config CIFS_ROOT
bool "SMB root file system (Experimental)"
depends on CIFS=y && IP_PNP
help
Enables root file system support over SMB protocol.

Most people say N here.
2 changes: 2 additions & 0 deletions fs/cifs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o dfs_cache.o
cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o

cifs-$(CONFIG_CIFS_SMB_DIRECT) += smbdirect.o

cifs-$(CONFIG_CIFS_ROOT) += cifsroot.o
2 changes: 2 additions & 0 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,7 @@ struct smb_vol {
__u32 handle_timeout; /* persistent and durable handle timeout in ms */
unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
__u16 compression; /* compression algorithm 0xFFFF default 0=disabled */
bool rootfs:1; /* if it's a SMB root file system */
};

/**
Expand Down Expand Up @@ -764,6 +765,7 @@ struct TCP_Server_Info {
* reconnect.
*/
int nr_targets;
bool noblockcnt; /* use non-blocking connect() */
};

struct cifs_credits {
Expand Down
83 changes: 83 additions & 0 deletions fs/cifs/cifsroot.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// SPDX-License-Identifier: GPL-2.0
/*
* SMB root file system support
*
* Copyright (c) 2019 Paulo Alcantara <[email protected]>
*/
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/root_dev.h>
#include <linux/kernel.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <net/ipconfig.h>

#define DEFAULT_MNT_OPTS \
"vers=1.0,cifsacl,mfsymlinks,rsize=1048576,wsize=65536,uid=0,gid=0," \
"hard,rootfs"

static char root_dev[2048] __initdata = "";
static char root_opts[1024] __initdata = DEFAULT_MNT_OPTS;

static __be32 __init parse_srvaddr(char *start, char *end)
{
char addr[sizeof("aaa.bbb.ccc.ddd")];
int i = 0;

while (start < end && i < sizeof(addr) - 1) {
if (isdigit(*start) || *start == '.')
addr[i++] = *start;
start++;
}
addr[i] = '\0';
return in_aton(addr);
}

/* cifsroot=//<server-ip>/<share>[,options] */
static int __init cifs_root_setup(char *line)
{
char *s;
int len;
__be32 srvaddr = htonl(INADDR_NONE);

ROOT_DEV = Root_CIFS;

if (strlen(line) > 3 && line[0] == '/' && line[1] == '/') {
s = strchr(&line[2], '/');
if (!s || s[1] == '\0')
return 1;

s = strchrnul(s, ',');
len = s - line + 1;
if (len <= sizeof(root_dev)) {
strlcpy(root_dev, line, len);
srvaddr = parse_srvaddr(&line[2], s);
if (*s) {
snprintf(root_opts, sizeof(root_opts), "%s,%s",
DEFAULT_MNT_OPTS, s + 1);
}
}
}

root_server_addr = srvaddr;

return 1;
}

__setup("cifsroot=", cifs_root_setup);

int __init cifs_root_data(char **dev, char **opts)
{
if (!root_dev[0] || root_server_addr == htonl(INADDR_NONE)) {
printk(KERN_ERR "Root-CIFS: no SMB server address\n");
return -1;
}

*dev = root_dev;
*opts = root_opts;

return 0;
}
17 changes: 14 additions & 3 deletions fs/cifs/connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ enum {
Opt_multiuser, Opt_sloppy, Opt_nosharesock,
Opt_persistent, Opt_nopersistent,
Opt_resilient, Opt_noresilient,
Opt_domainauto, Opt_rdma, Opt_modesid,
Opt_domainauto, Opt_rdma, Opt_modesid, Opt_rootfs,
Opt_compress,

/* Mount options which take numeric value */
Expand Down Expand Up @@ -266,6 +266,7 @@ static const match_table_t cifs_mount_option_tokens = {
{ Opt_ignore, "nomand" },
{ Opt_ignore, "relatime" },
{ Opt_ignore, "_netdev" },
{ Opt_rootfs, "rootfs" },

{ Opt_err, NULL }
};
Expand Down Expand Up @@ -1777,6 +1778,11 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
case Opt_nodfs:
vol->nodfs = 1;
break;
case Opt_rootfs:
#ifdef CONFIG_CIFS_ROOT
vol->rootfs = true;
#endif
break;
case Opt_posixpaths:
vol->posix_paths = 1;
break;
Expand Down Expand Up @@ -2727,7 +2733,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
goto out_err_crypto_release;
}

tcp_ses->noblocksnd = volume_info->noblocksnd;
tcp_ses->noblockcnt = volume_info->rootfs;
tcp_ses->noblocksnd = volume_info->noblocksnd || volume_info->rootfs;
tcp_ses->noautotune = volume_info->noautotune;
tcp_ses->tcp_nodelay = volume_info->sockopt_tcp_nodelay;
tcp_ses->rdma = volume_info->rdma;
Expand Down Expand Up @@ -3873,7 +3880,11 @@ generic_ip_connect(struct TCP_Server_Info *server)
socket->sk->sk_sndbuf,
socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo);

rc = socket->ops->connect(socket, saddr, slen, 0);
rc = socket->ops->connect(socket, saddr, slen,
server->noblockcnt ? O_NONBLOCK : 0);

if (rc == -EINPROGRESS)
rc = 0;
if (rc < 0) {
cifs_dbg(FYI, "Error %d connecting to server\n", rc);
sock_release(socket);
Expand Down
1 change: 1 addition & 0 deletions include/linux/root_dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

enum {
Root_NFS = MKDEV(UNNAMED_MAJOR, 255),
Root_CIFS = MKDEV(UNNAMED_MAJOR, 254),
Root_RAM0 = MKDEV(RAMDISK_MAJOR, 0),
Root_RAM1 = MKDEV(RAMDISK_MAJOR, 1),
Root_FD0 = MKDEV(FLOPPY_MAJOR, 0),
Expand Down

0 comments on commit 8eecd1c

Please sign in to comment.