Revert series: virtiofsd: Announce submounts to the guest
This reverts the following commits due to their basis on a bogus linux kernel header update:c93a656f7b
("tests/acceptance: Add virtiofs_submounts.py")45ced7ca2f
("tests/acceptance/boot_linux: Accept SSH pubkey")08dce386e7
("virtiofsd: Announce sub-mount points")eba8b096c1
("virtiofsd: Store every lo_inode's parent_dev")ede24b6be7
("virtiofsd: Add fuse_reply_attr_with_flags()")e2577435d3
("virtiofsd: Add attr_flags to fuse_entry_param")2f10415abf
("virtiofsd: Announce FUSE_ATTR_FLAGS")97d741cc96
("linux/fuse.h: Pull in from Linux") Cc: Max Reitz <mreitz@redhat.com> Cc: Stefan Hajnoczi <stefanha@redhat.com> Cc: Dr. David Alan Gilbert <dgilbert@redhat.com> Signed-off-by: Alex Williamson <alex.williamson@redhat.com> Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com> Reviewed-by: Max Reitz <mreitz@redhat.com> Message-id: 160385090886.20017.13382256442750027666.stgit@gimli.home Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
cddfbe0774
commit
33dc9914ea
@ -227,7 +227,7 @@ struct fuse_attr {
|
|||||||
uint32_t gid;
|
uint32_t gid;
|
||||||
uint32_t rdev;
|
uint32_t rdev;
|
||||||
uint32_t blksize;
|
uint32_t blksize;
|
||||||
uint32_t flags;
|
uint32_t padding;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct fuse_kstatfs {
|
struct fuse_kstatfs {
|
||||||
@ -310,7 +310,6 @@ struct fuse_file_lock {
|
|||||||
* FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir
|
* FUSE_NO_OPENDIR_SUPPORT: kernel supports zero-message opendir
|
||||||
* FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request
|
* FUSE_EXPLICIT_INVAL_DATA: only invalidate cached pages on explicit request
|
||||||
* FUSE_MAP_ALIGNMENT: map_alignment field is valid
|
* FUSE_MAP_ALIGNMENT: map_alignment field is valid
|
||||||
* FUSE_ATTR_FLAGS: fuse_attr.flags is present and valid
|
|
||||||
*/
|
*/
|
||||||
#define FUSE_ASYNC_READ (1 << 0)
|
#define FUSE_ASYNC_READ (1 << 0)
|
||||||
#define FUSE_POSIX_LOCKS (1 << 1)
|
#define FUSE_POSIX_LOCKS (1 << 1)
|
||||||
@ -339,7 +338,6 @@ struct fuse_file_lock {
|
|||||||
#define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
|
#define FUSE_NO_OPENDIR_SUPPORT (1 << 24)
|
||||||
#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
|
#define FUSE_EXPLICIT_INVAL_DATA (1 << 25)
|
||||||
#define FUSE_MAP_ALIGNMENT (1 << 26)
|
#define FUSE_MAP_ALIGNMENT (1 << 26)
|
||||||
#define FUSE_ATTR_FLAGS (1 << 27)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CUSE INIT request/reply flags
|
* CUSE INIT request/reply flags
|
||||||
@ -415,13 +413,6 @@ struct fuse_file_lock {
|
|||||||
*/
|
*/
|
||||||
#define FUSE_FSYNC_FDATASYNC (1 << 0)
|
#define FUSE_FSYNC_FDATASYNC (1 << 0)
|
||||||
|
|
||||||
/**
|
|
||||||
* fuse_attr flags
|
|
||||||
*
|
|
||||||
* FUSE_ATTR_SUBMOUNT: File/directory is a submount point
|
|
||||||
*/
|
|
||||||
#define FUSE_ATTR_SUBMOUNT (1 << 0)
|
|
||||||
|
|
||||||
enum fuse_opcode {
|
enum fuse_opcode {
|
||||||
FUSE_LOOKUP = 1,
|
FUSE_LOOKUP = 1,
|
||||||
FUSE_FORGET = 2, /* no reply */
|
FUSE_FORGET = 2, /* no reply */
|
||||||
|
@ -57,7 +57,7 @@ class BootLinuxBase(Test):
|
|||||||
self.cancel('Failed to download/prepare boot image')
|
self.cancel('Failed to download/prepare boot image')
|
||||||
return boot.path
|
return boot.path
|
||||||
|
|
||||||
def download_cloudinit(self, ssh_pubkey=None):
|
def download_cloudinit(self):
|
||||||
self.log.info('Preparing cloudinit image')
|
self.log.info('Preparing cloudinit image')
|
||||||
try:
|
try:
|
||||||
cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso')
|
cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso')
|
||||||
@ -67,8 +67,7 @@ class BootLinuxBase(Test):
|
|||||||
password='password',
|
password='password',
|
||||||
# QEMU's hard coded usermode router address
|
# QEMU's hard coded usermode router address
|
||||||
phone_home_host='10.0.2.2',
|
phone_home_host='10.0.2.2',
|
||||||
phone_home_port=self.phone_home_port,
|
phone_home_port=self.phone_home_port)
|
||||||
authorized_key=ssh_pubkey)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
self.cancel('Failed to prepared cloudinit image')
|
self.cancel('Failed to prepared cloudinit image')
|
||||||
return cloudinit_iso
|
return cloudinit_iso
|
||||||
@ -81,19 +80,19 @@ class BootLinux(BootLinuxBase):
|
|||||||
timeout = 900
|
timeout = 900
|
||||||
chksum = None
|
chksum = None
|
||||||
|
|
||||||
def setUp(self, ssh_pubkey=None):
|
def setUp(self):
|
||||||
super(BootLinux, self).setUp()
|
super(BootLinux, self).setUp()
|
||||||
self.vm.add_args('-smp', '2')
|
self.vm.add_args('-smp', '2')
|
||||||
self.vm.add_args('-m', '1024')
|
self.vm.add_args('-m', '1024')
|
||||||
self.prepare_boot()
|
self.prepare_boot()
|
||||||
self.prepare_cloudinit(ssh_pubkey)
|
self.prepare_cloudinit()
|
||||||
|
|
||||||
def prepare_boot(self):
|
def prepare_boot(self):
|
||||||
path = self.download_boot()
|
path = self.download_boot()
|
||||||
self.vm.add_args('-drive', 'file=%s' % path)
|
self.vm.add_args('-drive', 'file=%s' % path)
|
||||||
|
|
||||||
def prepare_cloudinit(self, ssh_pubkey=None):
|
def prepare_cloudinit(self):
|
||||||
cloudinit_iso = self.download_cloudinit(ssh_pubkey)
|
cloudinit_iso = self.download_cloudinit()
|
||||||
self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso)
|
self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso)
|
||||||
|
|
||||||
def launch_and_wait(self):
|
def launch_and_wait(self):
|
||||||
|
@ -1,289 +0,0 @@
|
|||||||
import logging
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import time
|
|
||||||
|
|
||||||
from avocado import skipUnless
|
|
||||||
from avocado_qemu import Test, BUILD_DIR
|
|
||||||
from avocado_qemu import wait_for_console_pattern
|
|
||||||
from avocado.utils import ssh
|
|
||||||
|
|
||||||
from qemu.accel import kvm_available
|
|
||||||
|
|
||||||
from boot_linux import BootLinux
|
|
||||||
|
|
||||||
|
|
||||||
def run_cmd(args):
|
|
||||||
subp = subprocess.Popen(args,
|
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
universal_newlines=True)
|
|
||||||
stdout, stderr = subp.communicate()
|
|
||||||
ret = subp.returncode
|
|
||||||
|
|
||||||
return (stdout, stderr, ret)
|
|
||||||
|
|
||||||
def has_passwordless_sudo():
|
|
||||||
"""
|
|
||||||
This function is for use in a @avocado.skipUnless decorator, e.g.:
|
|
||||||
|
|
||||||
@skipUnless(*has_passwordless_sudo())
|
|
||||||
def test_something_that_needs_sudo(self):
|
|
||||||
...
|
|
||||||
"""
|
|
||||||
|
|
||||||
_, stderr, exitcode = run_cmd(('sudo', '-n', 'true'))
|
|
||||||
if exitcode != 0:
|
|
||||||
return (False, f'Failed to use sudo -n: {stderr.strip()}')
|
|
||||||
else:
|
|
||||||
return (True, '')
|
|
||||||
|
|
||||||
|
|
||||||
class VirtiofsSubmountsTest(BootLinux):
|
|
||||||
"""
|
|
||||||
:avocado: tags=arch:x86_64
|
|
||||||
"""
|
|
||||||
|
|
||||||
def get_portfwd(self):
|
|
||||||
port = None
|
|
||||||
|
|
||||||
res = self.vm.command('human-monitor-command',
|
|
||||||
command_line='info usernet')
|
|
||||||
for line in res.split('\r\n'):
|
|
||||||
match = \
|
|
||||||
re.search(r'TCP.HOST_FORWARD.*127\.0\.0\.1\s*(\d+)\s+10\.',
|
|
||||||
line)
|
|
||||||
if match is not None:
|
|
||||||
port = match[1]
|
|
||||||
break
|
|
||||||
|
|
||||||
self.assertIsNotNone(port)
|
|
||||||
self.log.debug('sshd listening on port: ' + port)
|
|
||||||
return port
|
|
||||||
|
|
||||||
def ssh_connect(self, username, keyfile):
|
|
||||||
self.ssh_logger = logging.getLogger('ssh')
|
|
||||||
port = self.get_portfwd()
|
|
||||||
self.ssh_session = ssh.Session('127.0.0.1', port=int(port),
|
|
||||||
user=username, key=keyfile)
|
|
||||||
for i in range(10):
|
|
||||||
try:
|
|
||||||
self.ssh_session.connect()
|
|
||||||
return
|
|
||||||
except:
|
|
||||||
time.sleep(4)
|
|
||||||
pass
|
|
||||||
self.fail('sshd timeout')
|
|
||||||
|
|
||||||
def ssh_command(self, command):
|
|
||||||
self.ssh_logger.info(command)
|
|
||||||
result = self.ssh_session.cmd(command)
|
|
||||||
stdout_lines = [line.rstrip() for line
|
|
||||||
in result.stdout_text.splitlines()]
|
|
||||||
for line in stdout_lines:
|
|
||||||
self.ssh_logger.info(line)
|
|
||||||
stderr_lines = [line.rstrip() for line
|
|
||||||
in result.stderr_text.splitlines()]
|
|
||||||
for line in stderr_lines:
|
|
||||||
self.ssh_logger.warning(line)
|
|
||||||
|
|
||||||
self.assertEqual(result.exit_status, 0,
|
|
||||||
f'Guest command failed: {command}')
|
|
||||||
return stdout_lines, stderr_lines
|
|
||||||
|
|
||||||
def run(self, args, ignore_error=False):
|
|
||||||
stdout, stderr, ret = run_cmd(args)
|
|
||||||
|
|
||||||
if ret != 0:
|
|
||||||
cmdline = ' '.join(args)
|
|
||||||
if not ignore_error:
|
|
||||||
self.fail(f'{cmdline}: Returned {ret}: {stderr}')
|
|
||||||
else:
|
|
||||||
self.log.warn(f'{cmdline}: Returned {ret}: {stderr}')
|
|
||||||
|
|
||||||
return (stdout, stderr, ret)
|
|
||||||
|
|
||||||
def set_up_shared_dir(self):
|
|
||||||
atwd = os.getenv('AVOCADO_TEST_WORKDIR')
|
|
||||||
self.shared_dir = os.path.join(atwd, 'virtiofs-shared')
|
|
||||||
|
|
||||||
os.mkdir(self.shared_dir)
|
|
||||||
|
|
||||||
self.run(('cp', self.get_data('guest.sh'),
|
|
||||||
os.path.join(self.shared_dir, 'check.sh')))
|
|
||||||
|
|
||||||
self.run(('cp', self.get_data('guest-cleanup.sh'),
|
|
||||||
os.path.join(self.shared_dir, 'cleanup.sh')))
|
|
||||||
|
|
||||||
def set_up_virtiofs(self):
|
|
||||||
attmp = os.getenv('AVOCADO_TESTS_COMMON_TMPDIR')
|
|
||||||
self.vfsdsock = os.path.join(attmp, 'vfsdsock')
|
|
||||||
|
|
||||||
self.run(('sudo', '-n', 'rm', '-f', self.vfsdsock), ignore_error=True)
|
|
||||||
|
|
||||||
self.virtiofsd = \
|
|
||||||
subprocess.Popen(('sudo', '-n',
|
|
||||||
'tools/virtiofsd/virtiofsd',
|
|
||||||
f'--socket-path={self.vfsdsock}',
|
|
||||||
'-o', f'source={self.shared_dir}',
|
|
||||||
'-o', 'cache=always',
|
|
||||||
'-o', 'xattr',
|
|
||||||
'-o', 'announce_submounts',
|
|
||||||
'-f'),
|
|
||||||
stdout=subprocess.DEVNULL,
|
|
||||||
stderr=subprocess.PIPE,
|
|
||||||
universal_newlines=True)
|
|
||||||
|
|
||||||
while not os.path.exists(self.vfsdsock):
|
|
||||||
if self.virtiofsd.poll() is not None:
|
|
||||||
self.fail('virtiofsd exited prematurely: ' +
|
|
||||||
self.virtiofsd.communicate()[1])
|
|
||||||
time.sleep(0.1)
|
|
||||||
|
|
||||||
self.run(('sudo', '-n', 'chmod', 'go+rw', self.vfsdsock))
|
|
||||||
|
|
||||||
self.vm.add_args('-chardev',
|
|
||||||
f'socket,id=vfsdsock,path={self.vfsdsock}',
|
|
||||||
'-device',
|
|
||||||
'vhost-user-fs-pci,queue-size=1024,chardev=vfsdsock' \
|
|
||||||
',tag=host',
|
|
||||||
'-object',
|
|
||||||
'memory-backend-file,id=mem,size=1G,' \
|
|
||||||
'mem-path=/dev/shm,share=on',
|
|
||||||
'-numa',
|
|
||||||
'node,memdev=mem')
|
|
||||||
|
|
||||||
def launch_vm(self):
|
|
||||||
self.launch_and_wait()
|
|
||||||
self.ssh_connect('root', self.ssh_key)
|
|
||||||
|
|
||||||
def set_up_nested_mounts(self):
|
|
||||||
scratch_dir = os.path.join(self.shared_dir, 'scratch')
|
|
||||||
try:
|
|
||||||
os.mkdir(scratch_dir)
|
|
||||||
except FileExistsError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
args = ['bash', self.get_data('host.sh'), scratch_dir]
|
|
||||||
if self.seed:
|
|
||||||
args += [self.seed]
|
|
||||||
|
|
||||||
out, _, _ = self.run(args)
|
|
||||||
seed = re.search(r'^Seed: \d+', out)
|
|
||||||
self.log.info(seed[0])
|
|
||||||
|
|
||||||
def mount_in_guest(self):
|
|
||||||
self.ssh_command('mkdir -p /mnt/host')
|
|
||||||
self.ssh_command('mount -t virtiofs host /mnt/host')
|
|
||||||
|
|
||||||
def check_in_guest(self):
|
|
||||||
self.ssh_command('bash /mnt/host/check.sh /mnt/host/scratch/share')
|
|
||||||
|
|
||||||
def live_cleanup(self):
|
|
||||||
self.ssh_command('bash /mnt/host/cleanup.sh /mnt/host/scratch')
|
|
||||||
|
|
||||||
# It would be nice if the above was sufficient to make virtiofsd clear
|
|
||||||
# all references to the mounted directories (so they can be unmounted
|
|
||||||
# on the host), but unfortunately it is not. To do so, we have to
|
|
||||||
# resort to a remount.
|
|
||||||
self.ssh_command('mount -o remount /mnt/host')
|
|
||||||
|
|
||||||
scratch_dir = os.path.join(self.shared_dir, 'scratch')
|
|
||||||
self.run(('bash', self.get_data('cleanup.sh'), scratch_dir))
|
|
||||||
|
|
||||||
@skipUnless(*has_passwordless_sudo())
|
|
||||||
def setUp(self):
|
|
||||||
vmlinuz = self.params.get('vmlinuz')
|
|
||||||
if vmlinuz is None:
|
|
||||||
self.cancel('vmlinuz parameter not set; you must point it to a '
|
|
||||||
'Linux kernel binary to test (to run this test with ' \
|
|
||||||
'the on-image kernel, set it to an empty string)')
|
|
||||||
|
|
||||||
self.seed = self.params.get('seed')
|
|
||||||
|
|
||||||
atwd = os.getenv('AVOCADO_TEST_WORKDIR')
|
|
||||||
self.ssh_key = os.path.join(atwd, 'id_ed25519')
|
|
||||||
|
|
||||||
self.run(('ssh-keygen', '-t', 'ed25519', '-f', self.ssh_key))
|
|
||||||
|
|
||||||
pubkey = open(self.ssh_key + '.pub').read()
|
|
||||||
|
|
||||||
super(VirtiofsSubmountsTest, self).setUp(pubkey)
|
|
||||||
|
|
||||||
if len(vmlinuz) > 0:
|
|
||||||
self.vm.add_args('-kernel', vmlinuz,
|
|
||||||
'-append', 'console=ttyS0 root=/dev/sda1')
|
|
||||||
|
|
||||||
# Allow us to connect to SSH
|
|
||||||
self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22',
|
|
||||||
'-device', 'e1000,netdev=vnet')
|
|
||||||
|
|
||||||
if not kvm_available(self.arch, self.qemu_bin):
|
|
||||||
self.cancel(KVM_NOT_AVAILABLE)
|
|
||||||
self.vm.add_args('-accel', 'kvm')
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
try:
|
|
||||||
self.vm.shutdown()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
scratch_dir = os.path.join(self.shared_dir, 'scratch')
|
|
||||||
self.run(('bash', self.get_data('cleanup.sh'), scratch_dir),
|
|
||||||
ignore_error=True)
|
|
||||||
|
|
||||||
def test_pre_virtiofsd_set_up(self):
|
|
||||||
self.set_up_shared_dir()
|
|
||||||
|
|
||||||
self.set_up_nested_mounts()
|
|
||||||
|
|
||||||
self.set_up_virtiofs()
|
|
||||||
self.launch_vm()
|
|
||||||
self.mount_in_guest()
|
|
||||||
self.check_in_guest()
|
|
||||||
|
|
||||||
def test_pre_launch_set_up(self):
|
|
||||||
self.set_up_shared_dir()
|
|
||||||
self.set_up_virtiofs()
|
|
||||||
|
|
||||||
self.set_up_nested_mounts()
|
|
||||||
|
|
||||||
self.launch_vm()
|
|
||||||
self.mount_in_guest()
|
|
||||||
self.check_in_guest()
|
|
||||||
|
|
||||||
def test_post_launch_set_up(self):
|
|
||||||
self.set_up_shared_dir()
|
|
||||||
self.set_up_virtiofs()
|
|
||||||
self.launch_vm()
|
|
||||||
|
|
||||||
self.set_up_nested_mounts()
|
|
||||||
|
|
||||||
self.mount_in_guest()
|
|
||||||
self.check_in_guest()
|
|
||||||
|
|
||||||
def test_post_mount_set_up(self):
|
|
||||||
self.set_up_shared_dir()
|
|
||||||
self.set_up_virtiofs()
|
|
||||||
self.launch_vm()
|
|
||||||
self.mount_in_guest()
|
|
||||||
|
|
||||||
self.set_up_nested_mounts()
|
|
||||||
|
|
||||||
self.check_in_guest()
|
|
||||||
|
|
||||||
def test_two_runs(self):
|
|
||||||
self.set_up_shared_dir()
|
|
||||||
|
|
||||||
self.set_up_nested_mounts()
|
|
||||||
|
|
||||||
self.set_up_virtiofs()
|
|
||||||
self.launch_vm()
|
|
||||||
self.mount_in_guest()
|
|
||||||
self.check_in_guest()
|
|
||||||
|
|
||||||
self.live_cleanup()
|
|
||||||
self.set_up_nested_mounts()
|
|
||||||
|
|
||||||
self.check_in_guest()
|
|
@ -1,46 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
function print_usage()
|
|
||||||
{
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
echo "Error: $2"
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
echo "Usage: $1 <scratch dir>"
|
|
||||||
}
|
|
||||||
|
|
||||||
scratch_dir=$1
|
|
||||||
if [ -z "$scratch_dir" ]; then
|
|
||||||
print_usage "$0" 'Scratch dir not given' >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "$scratch_dir/share" || exit 1
|
|
||||||
mps=(mnt*)
|
|
||||||
mp_i=0
|
|
||||||
for mp in "${mps[@]}"; do
|
|
||||||
mp_i=$((mp_i + 1))
|
|
||||||
printf "Unmounting %i/%i...\r" "$mp_i" "${#mps[@]}"
|
|
||||||
|
|
||||||
sudo umount -R "$mp"
|
|
||||||
rm -rf "$mp"
|
|
||||||
done
|
|
||||||
echo
|
|
||||||
|
|
||||||
rm some-file
|
|
||||||
cd ..
|
|
||||||
rmdir share
|
|
||||||
|
|
||||||
imgs=(fs*.img)
|
|
||||||
img_i=0
|
|
||||||
for img in "${imgs[@]}"; do
|
|
||||||
img_i=$((img_i + 1))
|
|
||||||
printf "Detaching and deleting %i/%i...\r" "$img_i" "${#imgs[@]}"
|
|
||||||
|
|
||||||
dev=$(losetup -j "$img" | sed -e 's/:.*//')
|
|
||||||
sudo losetup -d "$dev"
|
|
||||||
rm -f "$img"
|
|
||||||
done
|
|
||||||
echo
|
|
||||||
|
|
||||||
echo 'Done.'
|
|
@ -1,30 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
function print_usage()
|
|
||||||
{
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
echo "Error: $2"
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
echo "Usage: $1 <scratch dir>"
|
|
||||||
}
|
|
||||||
|
|
||||||
scratch_dir=$1
|
|
||||||
if [ -z "$scratch_dir" ]; then
|
|
||||||
print_usage "$0" 'Scratch dir not given' >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "$scratch_dir/share" || exit 1
|
|
||||||
|
|
||||||
mps=(mnt*)
|
|
||||||
mp_i=0
|
|
||||||
for mp in "${mps[@]}"; do
|
|
||||||
mp_i=$((mp_i + 1))
|
|
||||||
printf "Unmounting %i/%i...\r" "$mp_i" "${#mps[@]}"
|
|
||||||
|
|
||||||
sudo umount -R "$mp"
|
|
||||||
done
|
|
||||||
echo
|
|
||||||
|
|
||||||
echo 'Done.'
|
|
@ -1,138 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
function print_usage()
|
|
||||||
{
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
echo "Error: $2"
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
echo "Usage: $1 <shared dir>"
|
|
||||||
echo '(The shared directory is the "share" directory in the scratch' \
|
|
||||||
'directory)'
|
|
||||||
}
|
|
||||||
|
|
||||||
shared_dir=$1
|
|
||||||
if [ -z "$shared_dir" ]; then
|
|
||||||
print_usage "$0" 'Shared dir not given' >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "$shared_dir"
|
|
||||||
|
|
||||||
# FIXME: This should not be necessary, but it is. In order for all
|
|
||||||
# submounts to be proper mount points, we need to visit them.
|
|
||||||
# (Before we visit them, they will not be auto-mounted, and so just
|
|
||||||
# appear as normal directories, with the catch that their st_ino will
|
|
||||||
# be the st_ino of the filesystem they host, while the st_dev will
|
|
||||||
# still be the st_dev of the parent.)
|
|
||||||
# `find` does not work, because it will refuse to touch the mount
|
|
||||||
# points as long as they are not mounted; their st_dev being shared
|
|
||||||
# with the parent and st_ino just being the root node's inode ID
|
|
||||||
# will practically ensure that this node exists elsewhere on the
|
|
||||||
# filesystem, and `find` is required to recognize loops and not to
|
|
||||||
# follow them.
|
|
||||||
# Thus, we have to manually visit all nodes first.
|
|
||||||
|
|
||||||
mnt_i=0
|
|
||||||
|
|
||||||
function recursively_visit()
|
|
||||||
{
|
|
||||||
pushd "$1" >/dev/null
|
|
||||||
for entry in *; do
|
|
||||||
if [[ "$entry" == mnt* ]]; then
|
|
||||||
mnt_i=$((mnt_i + 1))
|
|
||||||
printf "Triggering auto-mount $mnt_i...\r"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -d "$entry" ]; then
|
|
||||||
recursively_visit "$entry"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
popd >/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
recursively_visit .
|
|
||||||
echo
|
|
||||||
|
|
||||||
|
|
||||||
if [ -n "$(find -name not-mounted)" ]; then
|
|
||||||
echo "Error: not-mounted files visible on mount points:" >&2
|
|
||||||
find -name not-mounted >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f some-file -o "$(cat some-file)" != 'root' ]; then
|
|
||||||
echo "Error: Bad file in the share root" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
shopt -s nullglob
|
|
||||||
|
|
||||||
function check_submounts()
|
|
||||||
{
|
|
||||||
local base_path=$1
|
|
||||||
|
|
||||||
for mp in mnt*; do
|
|
||||||
printf "Checking submount %i...\r" "$((${#devs[@]} + 1))"
|
|
||||||
|
|
||||||
mp_i=$(echo "$mp" | sed -e 's/mnt//')
|
|
||||||
dev=$(stat -c '%D' "$mp")
|
|
||||||
|
|
||||||
if [ -n "${devs[mp_i]}" ]; then
|
|
||||||
echo "Error: $mp encountered twice" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
devs[mp_i]=$dev
|
|
||||||
|
|
||||||
pushd "$mp" >/dev/null
|
|
||||||
path="$base_path$mp"
|
|
||||||
while true; do
|
|
||||||
expected_content="$(printf '%s\n%s\n' "$mp_i" "$path")"
|
|
||||||
if [ ! -f some-file ]; then
|
|
||||||
echo "Error: $PWD/some-file does not exist" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$(cat some-file)" != "$expected_content" ]; then
|
|
||||||
echo "Error: Bad content in $PWD/some-file:" >&2
|
|
||||||
echo '--- found ---'
|
|
||||||
cat some-file
|
|
||||||
echo '--- expected ---'
|
|
||||||
echo "$expected_content"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ "$(stat -c '%D' some-file)" != "$dev" ]; then
|
|
||||||
echo "Error: $PWD/some-file has the wrong device ID" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -d sub ]; then
|
|
||||||
if [ "$(stat -c '%D' sub)" != "$dev" ]; then
|
|
||||||
echo "Error: $PWD/some-file has the wrong device ID" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
cd sub
|
|
||||||
path="$path/sub"
|
|
||||||
else
|
|
||||||
if [ -n "$(echo mnt*)" ]; then
|
|
||||||
check_submounts "$path/"
|
|
||||||
fi
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
popd >/dev/null
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
root_dev=$(stat -c '%D' some-file)
|
|
||||||
devs=()
|
|
||||||
check_submounts ''
|
|
||||||
echo
|
|
||||||
|
|
||||||
reused_devs=$(echo "$root_dev ${devs[@]}" | tr ' ' '\n' | sort | uniq -d)
|
|
||||||
if [ -n "$reused_devs" ]; then
|
|
||||||
echo "Error: Reused device IDs: $reused_devs" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Test passed for ${#devs[@]} submounts."
|
|
@ -1,127 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
mount_count=128
|
|
||||||
|
|
||||||
function print_usage()
|
|
||||||
{
|
|
||||||
if [ -n "$2" ]; then
|
|
||||||
echo "Error: $2"
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
echo "Usage: $1 <scratch dir> [seed]"
|
|
||||||
echo "(If no seed is given, it will be randomly generated.)"
|
|
||||||
}
|
|
||||||
|
|
||||||
scratch_dir=$1
|
|
||||||
if [ -z "$scratch_dir" ]; then
|
|
||||||
print_usage "$0" 'No scratch dir given' >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -d "$scratch_dir" ]; then
|
|
||||||
print_usage "$0" "$scratch_dir is not a directory" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
seed=$2
|
|
||||||
if [ -z "$seed" ]; then
|
|
||||||
seed=$RANDOM
|
|
||||||
fi
|
|
||||||
RANDOM=$seed
|
|
||||||
|
|
||||||
echo "Seed: $seed"
|
|
||||||
|
|
||||||
set -e
|
|
||||||
shopt -s nullglob
|
|
||||||
|
|
||||||
cd "$scratch_dir"
|
|
||||||
if [ -d share ]; then
|
|
||||||
echo 'Error: This directory seems to be in use already' >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
for ((i = 0; i < $mount_count; i++)); do
|
|
||||||
printf "Setting up fs %i/%i...\r" "$((i + 1))" "$mount_count"
|
|
||||||
|
|
||||||
rm -f fs$i.img
|
|
||||||
truncate -s 512M fs$i.img
|
|
||||||
mkfs.xfs -q fs$i.img
|
|
||||||
devs[i]=$(sudo losetup -f --show fs$i.img)
|
|
||||||
done
|
|
||||||
echo
|
|
||||||
|
|
||||||
top_level_mounts=$((RANDOM % mount_count + 1))
|
|
||||||
|
|
||||||
mkdir -p share
|
|
||||||
echo 'root' > share/some-file
|
|
||||||
|
|
||||||
for ((i = 0; i < $top_level_mounts; i++)); do
|
|
||||||
printf "Mounting fs %i/%i...\r" "$((i + 1))" "$mount_count"
|
|
||||||
|
|
||||||
mkdir -p share/mnt$i
|
|
||||||
touch share/mnt$i/not-mounted
|
|
||||||
sudo mount "${devs[i]}" share/mnt$i
|
|
||||||
sudo chown "$(id -u):$(id -g)" share/mnt$i
|
|
||||||
|
|
||||||
pushd share/mnt$i >/dev/null
|
|
||||||
path=mnt$i
|
|
||||||
nesting=$((RANDOM % 4))
|
|
||||||
for ((j = 0; j < $nesting; j++)); do
|
|
||||||
cat > some-file <<EOF
|
|
||||||
$i
|
|
||||||
$path
|
|
||||||
EOF
|
|
||||||
mkdir sub
|
|
||||||
cd sub
|
|
||||||
path="$path/sub"
|
|
||||||
done
|
|
||||||
cat > some-file <<EOF
|
|
||||||
$i
|
|
||||||
$path
|
|
||||||
EOF
|
|
||||||
popd >/dev/null
|
|
||||||
done
|
|
||||||
|
|
||||||
for ((; i < $mount_count; i++)); do
|
|
||||||
printf "Mounting fs %i/%i...\r" "$((i + 1))" "$mount_count"
|
|
||||||
|
|
||||||
mp_i=$((i % top_level_mounts))
|
|
||||||
|
|
||||||
pushd share/mnt$mp_i >/dev/null
|
|
||||||
path=mnt$mp_i
|
|
||||||
while true; do
|
|
||||||
sub_mp="$(echo mnt*)"
|
|
||||||
if cd sub 2>/dev/null; then
|
|
||||||
path="$path/sub"
|
|
||||||
elif [ -n "$sub_mp" ] && cd "$sub_mp" 2>/dev/null; then
|
|
||||||
path="$path/$sub_mp"
|
|
||||||
else
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
mkdir mnt$i
|
|
||||||
touch mnt$i/not-mounted
|
|
||||||
sudo mount "${devs[i]}" mnt$i
|
|
||||||
sudo chown "$(id -u):$(id -g)" mnt$i
|
|
||||||
|
|
||||||
cd mnt$i
|
|
||||||
path="$path/mnt$i"
|
|
||||||
nesting=$((RANDOM % 4))
|
|
||||||
for ((j = 0; j < $nesting; j++)); do
|
|
||||||
cat > some-file <<EOF
|
|
||||||
$i
|
|
||||||
$path
|
|
||||||
EOF
|
|
||||||
mkdir sub
|
|
||||||
cd sub
|
|
||||||
path="$path/sub"
|
|
||||||
done
|
|
||||||
cat > some-file <<EOF
|
|
||||||
$i
|
|
||||||
$path
|
|
||||||
EOF
|
|
||||||
popd >/dev/null
|
|
||||||
done
|
|
||||||
echo
|
|
||||||
|
|
||||||
echo 'Done.'
|
|
@ -352,14 +352,6 @@ struct fuse_file_info {
|
|||||||
*/
|
*/
|
||||||
#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24)
|
#define FUSE_CAP_NO_OPENDIR_SUPPORT (1 << 24)
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates that the client will provide fuse_attr.flags, and the kernel will
|
|
||||||
* interpret it.
|
|
||||||
*
|
|
||||||
* This feature is enabled by default when supported by the kernel.
|
|
||||||
*/
|
|
||||||
#define FUSE_CAP_ATTR_FLAGS (1 << 27)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ioctl flags
|
* Ioctl flags
|
||||||
*
|
*
|
||||||
|
@ -329,8 +329,7 @@ static unsigned int calc_timeout_nsec(double t)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fill_entry(struct fuse_session *se,
|
static void fill_entry(struct fuse_entry_out *arg,
|
||||||
struct fuse_entry_out *arg,
|
|
||||||
const struct fuse_entry_param *e)
|
const struct fuse_entry_param *e)
|
||||||
{
|
{
|
||||||
*arg = (struct fuse_entry_out){
|
*arg = (struct fuse_entry_out){
|
||||||
@ -342,10 +341,6 @@ static void fill_entry(struct fuse_session *se,
|
|||||||
.attr_valid_nsec = calc_timeout_nsec(e->attr_timeout),
|
.attr_valid_nsec = calc_timeout_nsec(e->attr_timeout),
|
||||||
};
|
};
|
||||||
convert_stat(&e->attr, &arg->attr);
|
convert_stat(&e->attr, &arg->attr);
|
||||||
|
|
||||||
if (se->conn.capable & FUSE_CAP_ATTR_FLAGS) {
|
|
||||||
arg->attr.flags = e->attr_flags;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -370,7 +365,7 @@ size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
|
|||||||
|
|
||||||
struct fuse_direntplus *dp = (struct fuse_direntplus *)buf;
|
struct fuse_direntplus *dp = (struct fuse_direntplus *)buf;
|
||||||
memset(&dp->entry_out, 0, sizeof(dp->entry_out));
|
memset(&dp->entry_out, 0, sizeof(dp->entry_out));
|
||||||
fill_entry(req->se, &dp->entry_out, e);
|
fill_entry(&dp->entry_out, e);
|
||||||
|
|
||||||
struct fuse_dirent *dirent = &dp->dirent;
|
struct fuse_dirent *dirent = &dp->dirent;
|
||||||
*dirent = (struct fuse_dirent){
|
*dirent = (struct fuse_dirent){
|
||||||
@ -408,7 +403,7 @@ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
|
|||||||
size_t size = sizeof(arg);
|
size_t size = sizeof(arg);
|
||||||
|
|
||||||
memset(&arg, 0, sizeof(arg));
|
memset(&arg, 0, sizeof(arg));
|
||||||
fill_entry(req->se, &arg, e);
|
fill_entry(&arg, e);
|
||||||
return send_reply_ok(req, &arg, size);
|
return send_reply_ok(req, &arg, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,13 +416,13 @@ int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
|
|||||||
struct fuse_open_out *oarg = (struct fuse_open_out *)(buf + entrysize);
|
struct fuse_open_out *oarg = (struct fuse_open_out *)(buf + entrysize);
|
||||||
|
|
||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
fill_entry(req->se, earg, e);
|
fill_entry(earg, e);
|
||||||
fill_open(oarg, f);
|
fill_open(oarg, f);
|
||||||
return send_reply_ok(req, buf, entrysize + sizeof(struct fuse_open_out));
|
return send_reply_ok(req, buf, entrysize + sizeof(struct fuse_open_out));
|
||||||
}
|
}
|
||||||
|
|
||||||
int fuse_reply_attr_with_flags(fuse_req_t req, const struct stat *attr,
|
int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
|
||||||
double attr_timeout, uint32_t attr_flags)
|
double attr_timeout)
|
||||||
{
|
{
|
||||||
struct fuse_attr_out arg;
|
struct fuse_attr_out arg;
|
||||||
size_t size = sizeof(arg);
|
size_t size = sizeof(arg);
|
||||||
@ -437,19 +432,9 @@ int fuse_reply_attr_with_flags(fuse_req_t req, const struct stat *attr,
|
|||||||
arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
|
arg.attr_valid_nsec = calc_timeout_nsec(attr_timeout);
|
||||||
convert_stat(attr, &arg.attr);
|
convert_stat(attr, &arg.attr);
|
||||||
|
|
||||||
if (req->se->conn.capable & FUSE_CAP_ATTR_FLAGS) {
|
|
||||||
arg.attr.flags = attr_flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
return send_reply_ok(req, &arg, size);
|
return send_reply_ok(req, &arg, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
|
|
||||||
double attr_timeout)
|
|
||||||
{
|
|
||||||
return fuse_reply_attr_with_flags(req, attr, attr_timeout, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int fuse_reply_readlink(fuse_req_t req, const char *linkname)
|
int fuse_reply_readlink(fuse_req_t req, const char *linkname)
|
||||||
{
|
{
|
||||||
return send_reply_ok(req, linkname, strlen(linkname));
|
return send_reply_ok(req, linkname, strlen(linkname));
|
||||||
@ -2003,9 +1988,6 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid,
|
|||||||
bufsize = max_bufsize;
|
bufsize = max_bufsize;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (arg->flags & FUSE_ATTR_FLAGS) {
|
|
||||||
se->conn.capable |= FUSE_CAP_ATTR_FLAGS;
|
|
||||||
}
|
|
||||||
#ifdef HAVE_SPLICE
|
#ifdef HAVE_SPLICE
|
||||||
#ifdef HAVE_VMSPLICE
|
#ifdef HAVE_VMSPLICE
|
||||||
se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
|
se->conn.capable |= FUSE_CAP_SPLICE_WRITE | FUSE_CAP_SPLICE_MOVE;
|
||||||
@ -2032,7 +2014,6 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid,
|
|||||||
LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO);
|
LL_SET_DEFAULT(1, FUSE_CAP_ASYNC_DIO);
|
||||||
LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR);
|
LL_SET_DEFAULT(1, FUSE_CAP_IOCTL_DIR);
|
||||||
LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC);
|
LL_SET_DEFAULT(1, FUSE_CAP_ATOMIC_O_TRUNC);
|
||||||
LL_SET_DEFAULT(1, FUSE_CAP_ATTR_FLAGS);
|
|
||||||
LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ);
|
LL_SET_DEFAULT(se->op.write_buf, FUSE_CAP_SPLICE_READ);
|
||||||
LL_SET_DEFAULT(se->op.getlk && se->op.setlk, FUSE_CAP_POSIX_LOCKS);
|
LL_SET_DEFAULT(se->op.getlk && se->op.setlk, FUSE_CAP_POSIX_LOCKS);
|
||||||
LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS);
|
LL_SET_DEFAULT(se->op.flock, FUSE_CAP_FLOCK_LOCKS);
|
||||||
@ -2122,9 +2103,6 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid,
|
|||||||
if (se->conn.want & FUSE_CAP_POSIX_ACL) {
|
if (se->conn.want & FUSE_CAP_POSIX_ACL) {
|
||||||
outarg.flags |= FUSE_POSIX_ACL;
|
outarg.flags |= FUSE_POSIX_ACL;
|
||||||
}
|
}
|
||||||
if (se->conn.want & FUSE_CAP_ATTR_FLAGS) {
|
|
||||||
outarg.flags |= FUSE_ATTR_FLAGS;
|
|
||||||
}
|
|
||||||
outarg.max_readahead = se->conn.max_readahead;
|
outarg.max_readahead = se->conn.max_readahead;
|
||||||
outarg.max_write = se->conn.max_write;
|
outarg.max_write = se->conn.max_write;
|
||||||
if (se->conn.max_background >= (1 << 16)) {
|
if (se->conn.max_background >= (1 << 16)) {
|
||||||
|
@ -102,11 +102,6 @@ struct fuse_entry_param {
|
|||||||
* large value.
|
* large value.
|
||||||
*/
|
*/
|
||||||
double entry_timeout;
|
double entry_timeout;
|
||||||
|
|
||||||
/**
|
|
||||||
* Flags for fuse_attr.flags that do not fit into attr.
|
|
||||||
*/
|
|
||||||
uint32_t attr_flags;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1313,21 +1308,6 @@ int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e,
|
|||||||
int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
|
int fuse_reply_attr(fuse_req_t req, const struct stat *attr,
|
||||||
double attr_timeout);
|
double attr_timeout);
|
||||||
|
|
||||||
/**
|
|
||||||
* Reply with attributes and set fuse_attr.flags
|
|
||||||
*
|
|
||||||
* Possible requests:
|
|
||||||
* getattr, setattr
|
|
||||||
*
|
|
||||||
* @param req request handle
|
|
||||||
* @param attr the attributes
|
|
||||||
* @param attr_timeout validity timeout (in seconds) for the attributes
|
|
||||||
* @param attr_flags flags to put into fuse_attr.flags
|
|
||||||
* @return zero for success, -errno for failure to send reply
|
|
||||||
*/
|
|
||||||
int fuse_reply_attr_with_flags(fuse_req_t req, const struct stat *attr,
|
|
||||||
double attr_timeout, uint32_t attr_flags);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reply with the contents of a symbolic link
|
* Reply with the contents of a symbolic link
|
||||||
*
|
*
|
||||||
|
@ -178,7 +178,6 @@ void fuse_cmdline_help(void)
|
|||||||
" default: depends on cache= option.\n"
|
" default: depends on cache= option.\n"
|
||||||
" -o writeback|no_writeback enable/disable writeback cache\n"
|
" -o writeback|no_writeback enable/disable writeback cache\n"
|
||||||
" default: no_writeback\n"
|
" default: no_writeback\n"
|
||||||
" -o announce_submounts Announce sub-mount points to the guest\n"
|
|
||||||
" -o xattr|no_xattr enable/disable xattr\n"
|
" -o xattr|no_xattr enable/disable xattr\n"
|
||||||
" default: no_xattr\n"
|
" default: no_xattr\n"
|
||||||
" -o modcaps=CAPLIST Modify the list of capabilities\n"
|
" -o modcaps=CAPLIST Modify the list of capabilities\n"
|
||||||
|
@ -40,7 +40,6 @@
|
|||||||
#include "fuse_virtio.h"
|
#include "fuse_virtio.h"
|
||||||
#include "fuse_log.h"
|
#include "fuse_log.h"
|
||||||
#include "fuse_lowlevel.h"
|
#include "fuse_lowlevel.h"
|
||||||
#include "standard-headers/linux/fuse.h"
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <cap-ng.h>
|
#include <cap-ng.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
@ -126,14 +125,6 @@ struct lo_inode {
|
|||||||
GHashTable *posix_locks; /* protected by lo_inode->plock_mutex */
|
GHashTable *posix_locks; /* protected by lo_inode->plock_mutex */
|
||||||
|
|
||||||
mode_t filetype;
|
mode_t filetype;
|
||||||
|
|
||||||
/*
|
|
||||||
* So we can detect crossmount roots
|
|
||||||
* (As such, this only needs to be valid for directories. Note
|
|
||||||
* that files can have multiple parents due to hard links, and so
|
|
||||||
* their parent_dev may fluctuate.)
|
|
||||||
*/
|
|
||||||
dev_t parent_dev;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lo_cred {
|
struct lo_cred {
|
||||||
@ -174,7 +165,6 @@ struct lo_data {
|
|||||||
int timeout_set;
|
int timeout_set;
|
||||||
int readdirplus_set;
|
int readdirplus_set;
|
||||||
int readdirplus_clear;
|
int readdirplus_clear;
|
||||||
int announce_submounts;
|
|
||||||
int allow_direct_io;
|
int allow_direct_io;
|
||||||
struct lo_inode root;
|
struct lo_inode root;
|
||||||
GHashTable *inodes; /* protected by lo->mutex */
|
GHashTable *inodes; /* protected by lo->mutex */
|
||||||
@ -213,7 +203,6 @@ static const struct fuse_opt lo_opts[] = {
|
|||||||
{ "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS },
|
{ "cache=always", offsetof(struct lo_data, cache), CACHE_ALWAYS },
|
||||||
{ "readdirplus", offsetof(struct lo_data, readdirplus_set), 1 },
|
{ "readdirplus", offsetof(struct lo_data, readdirplus_set), 1 },
|
||||||
{ "no_readdirplus", offsetof(struct lo_data, readdirplus_clear), 1 },
|
{ "no_readdirplus", offsetof(struct lo_data, readdirplus_clear), 1 },
|
||||||
{ "announce_submounts", offsetof(struct lo_data, announce_submounts), 1 },
|
|
||||||
{ "allow_direct_io", offsetof(struct lo_data, allow_direct_io), 1 },
|
{ "allow_direct_io", offsetof(struct lo_data, allow_direct_io), 1 },
|
||||||
{ "no_allow_direct_io", offsetof(struct lo_data, allow_direct_io), 0 },
|
{ "no_allow_direct_io", offsetof(struct lo_data, allow_direct_io), 0 },
|
||||||
FUSE_OPT_END
|
FUSE_OPT_END
|
||||||
@ -611,52 +600,22 @@ static void lo_init(void *userdata, struct fuse_conn_info *conn)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Call fstatat() and set st_rdev whenever a directory's st_dev
|
|
||||||
* differs from the rparent's st_dev (@parent_dev). This will
|
|
||||||
* announce submounts to the FUSE client (unless @announce_submounts
|
|
||||||
* is false).
|
|
||||||
*/
|
|
||||||
static int do_fstatat(int dirfd, const char *pathname, struct stat *statbuf,
|
|
||||||
int flags, dev_t parent_dev, uint32_t *fuse_attr_flags)
|
|
||||||
{
|
|
||||||
int res = fstatat(dirfd, pathname, statbuf, flags);
|
|
||||||
if (res == -1) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (statbuf->st_dev != parent_dev && S_ISDIR(statbuf->st_mode) &&
|
|
||||||
fuse_attr_flags)
|
|
||||||
{
|
|
||||||
*fuse_attr_flags |= FUSE_ATTR_SUBMOUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
|
static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
|
||||||
struct fuse_file_info *fi)
|
struct fuse_file_info *fi)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
struct stat buf;
|
struct stat buf;
|
||||||
struct lo_data *lo = lo_data(req);
|
struct lo_data *lo = lo_data(req);
|
||||||
struct lo_inode *inode = lo_inode(req, ino);
|
|
||||||
uint32_t fuse_attr_flags = 0;
|
|
||||||
|
|
||||||
(void)fi;
|
(void)fi;
|
||||||
|
|
||||||
res = do_fstatat(inode->fd, "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW,
|
res =
|
||||||
inode->parent_dev, &fuse_attr_flags);
|
fstatat(lo_fd(req, ino), "", &buf, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
||||||
lo_inode_put(lo, &inode);
|
|
||||||
if (res == -1) {
|
if (res == -1) {
|
||||||
return (void)fuse_reply_err(req, errno);
|
return (void)fuse_reply_err(req, errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lo->announce_submounts) {
|
fuse_reply_attr(req, &buf, lo->timeout);
|
||||||
fuse_attr_flags &= ~FUSE_ATTR_SUBMOUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
fuse_reply_attr_with_flags(req, &buf, lo->timeout, fuse_attr_flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int lo_fi_fd(fuse_req_t req, struct fuse_file_info *fi)
|
static int lo_fi_fd(fuse_req_t req, struct fuse_file_info *fi)
|
||||||
@ -852,16 +811,11 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
|
|||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = do_fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW,
|
res = fstatat(newfd, "", &e->attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
||||||
dir->key.dev, &e->attr_flags);
|
|
||||||
if (res == -1) {
|
if (res == -1) {
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lo->announce_submounts) {
|
|
||||||
e->attr_flags &= ~FUSE_ATTR_SUBMOUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
inode = lo_find(lo, &e->attr);
|
inode = lo_find(lo, &e->attr);
|
||||||
if (inode) {
|
if (inode) {
|
||||||
close(newfd);
|
close(newfd);
|
||||||
@ -893,7 +847,6 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name,
|
|||||||
g_hash_table_insert(lo->inodes, &inode->key, inode);
|
g_hash_table_insert(lo->inodes, &inode->key, inode);
|
||||||
pthread_mutex_unlock(&lo->mutex);
|
pthread_mutex_unlock(&lo->mutex);
|
||||||
}
|
}
|
||||||
inode->parent_dev = dir->key.dev;
|
|
||||||
e->ino = inode->fuse_ino;
|
e->ino = inode->fuse_ino;
|
||||||
lo_inode_put(lo, &inode);
|
lo_inode_put(lo, &inode);
|
||||||
lo_inode_put(lo, &dir);
|
lo_inode_put(lo, &dir);
|
||||||
@ -1107,17 +1060,11 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
|
|||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = do_fstatat(inode->fd, "", &e.attr,
|
res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
||||||
AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW,
|
|
||||||
parent_inode->key.dev, &e.attr_flags);
|
|
||||||
if (res == -1) {
|
if (res == -1) {
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!lo->announce_submounts) {
|
|
||||||
e.attr_flags &= ~FUSE_ATTR_SUBMOUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_lock(&lo->mutex);
|
pthread_mutex_lock(&lo->mutex);
|
||||||
inode->nlookup++;
|
inode->nlookup++;
|
||||||
pthread_mutex_unlock(&lo->mutex);
|
pthread_mutex_unlock(&lo->mutex);
|
||||||
@ -1126,14 +1073,6 @@ static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
|
|||||||
fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long)parent,
|
fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long)parent,
|
||||||
name, (unsigned long long)e.ino);
|
name, (unsigned long long)e.ino);
|
||||||
|
|
||||||
/*
|
|
||||||
* No need to update inode->parent_dev, because
|
|
||||||
* (1) We cannot, the inode now has more than one parent,
|
|
||||||
* (2) Directories cannot have more than one parent, so link()
|
|
||||||
* does not work for them; but parent_dev only needs to be
|
|
||||||
* valid for directories.
|
|
||||||
*/
|
|
||||||
|
|
||||||
fuse_reply_entry(req, &e);
|
fuse_reply_entry(req, &e);
|
||||||
lo_inode_put(lo, &parent_inode);
|
lo_inode_put(lo, &parent_inode);
|
||||||
lo_inode_put(lo, &inode);
|
lo_inode_put(lo, &inode);
|
||||||
@ -1152,21 +1091,14 @@ static struct lo_inode *lookup_name(fuse_req_t req, fuse_ino_t parent,
|
|||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
struct stat attr;
|
struct stat attr;
|
||||||
struct lo_data *lo = lo_data(req);
|
|
||||||
struct lo_inode *dir = lo_inode(req, parent);
|
|
||||||
|
|
||||||
if (!dir) {
|
res = fstatat(lo_fd(req, parent), name, &attr,
|
||||||
return NULL;
|
AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
|
||||||
}
|
|
||||||
|
|
||||||
res = do_fstatat(dir->fd, name, &attr,
|
|
||||||
AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW, dir->key.dev, NULL);
|
|
||||||
lo_inode_put(lo, &dir);
|
|
||||||
if (res == -1) {
|
if (res == -1) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return lo_find(lo, &attr);
|
return lo_find(lo_data(req), &attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
|
static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
|
||||||
|
Loading…
Reference in New Issue
Block a user