josch josch - 3 months ago 15
Linux Question

Executing mount system call from perl

How can the

mount
system call be done from perl? The following:

$ret = syscall(&SYS_mount, "/proc", "/path/to/my/mount/point", 0, 0, 0);


results in:

Modification of a read-only value attempted at ...


I cannot call the
mount
program using
system
because I need to make a
mount()
system call that the
mount
program doesn't seem to be capable of. More specifically, I need to call:

mount("/proc", "/path/to/my/mpoint/point", NULL, MS_REC|MS_PRIVATE|MS_BIND, NULL);


But if I try to run the following with an unprivileged unshared mount linux namespace:

mount --make-rprivate --bind /proc /path/to/my/mountpoint


Then I get the following error:

mount: wrong fs type, bad option, bad superblock on /proc,
missing codepage or helper program, or other error

In some cases useful info is found in syslog - try
dmesg | tail or so.


Using
strace
reveals that what the
mount
program actually does is to call:

mount("/proc", "/path/to/my/mountpoint", ..., MS_MGC_VAL|MS_BIND, NULL);
mount("none", "/path/to/my/mointpoint", NULL, MS_REC|MS_PRIVATE, NULL);


But this splitting of options does not work. I need
MS_BIND
and
MS_REC|MS_PRIVATE
in a single call to the
mount
system call for it to work in an unprivileged unshared mount namespace.

So how can I do my initial system call in perl without the error message about an attempt of a modification of a read-only value?

edit:

Thankfully ikegami was quick to point out what i did wrong when trying to use perl's
syscall
function but in case somebody finds this when searching for how to bind mount a directory from within an unprivileged mount namespace with just the
mount
command line utility, here is how:

mount --rbind /proc /path/to/my/mountpoint


This in turn will call the following syscall internally:

mount("proc", "/path/to/my/mountpoint", ..., MS_MGC_VAL|MS_BIND|MS_REC, 0);


The
MS_MGC_VAL
flag just seems to be for backwards compatibility with kernel versions prior to 2.4. The important bits are
MS_BIND
(for doing the bind mounting itself) and
MS_REC
(for doing it recursively so that directories that have their content hidden by other mounts being done into them do not have their content exposed in the mount namespace).

So now I have to decide whether I go with a perl
system
function call or just do the
mount
systemcall because both work just as well :)

Answer

syscall refuses to pass a pointer to the string buffer of a constant since it has no idea if the argument is char * or const char *.

You can't use a string literal (or other read-only string) as an argument to syscall because Perl has to assume that any string pointer might be written through

The solution is simple. Just copy the constant into a variable first.

my $ret = syscall(&SYS_mount, my $s="/proc", my $t="/path/to/my/mount/point", 0, 0, 0);

Test:

$ perl -E'
   require "syscall.ph";
   my $ret = syscall(&SYS_mount, "/proc", "/path/to/my/mount/point", 0, 0, 0);
   say $ret;
'
Modification of a read-only value attempted at -e line 3.

$ perl -E'
   require "syscall.ph";
   my $ret = syscall(&SYS_mount, my $s="/proc", my $t="/path/to/my/mount/point", 0, 0, 0);
   say $ret;
'
-1

$ strace perl -e'
   require "syscall.ph";
   syscall(&SYS_mount, my $s="/proc", my $t="/path/to/my/mount/point", 0, 0, 0);
' 2>&1 | grep mount
mount("/proc", "/path/to/my/mount/point", NULL, 0, NULL) = -1 ENOENT (No such file or directory)