Reputation: 8268
I am stuck in a strange problem.
I have two scripts (C program executables) running on ARM linux machine that are mounting the same USB device (containing chinese character filenames) on two different paths, as soon as the device is inserted.
int mount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags,
const void *data);
In the last parameter, Script A passes "utf8" and Script B passes 0.
So, as soon as I insert the USB device, the scripts race to mount the device.
If Script A mounts first (which passes utf8 parameter), I get proper filenames. This is the mount
command output [Notice that even second mount has utf8 as parameter, even if its not passed. Why?]
/dev/sdb1 on /home/root/script1 type vfat (ro,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-r
o)
/dev/sdb1 on /home/root/script2 type vfat (ro,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed
,utf8,errors=remount-ro)
But if script B mounts first(which passes 0 as last parameter to mount), I get broken filenames ?????.mp3
from readdir()
. This is the mount
command output.
/dev/sdb1 on /home/root/script2 type vfat (ro,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed,errors=remount-ro)
/dev/sdb1 on /home/root/script1 type vfat (ro,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=iso8859-1,shortname=mixed
,errors=remount-ro)
EDIT
This is the basic mount code of both the scripts developed for testing(only difference in last mount argument). Both scripts are executed immediately on reboot using a service.
//mount the device
ret = mount("/dev/sda1", "/home/root/script1/", "vfat", 1, "utf8");
if (ret == 0) {
fprintf(stdout,"mount() succeeded.\n");
sleep(2000);
} else {
ret = mount("/dev/sdb1", "/home/root/script1/", "vfat", 1, "utf8");
if(ret == 0)
{
fprintf(stdout,"mount() succeeded\n");
sleep(2000);
}
else
{
fprintf(stdout,"/dev/sdb1 mount() failed: %d, %s\n", errno, strerror(errno));
ret = mount("/dev/sdc1", "/home/root/script1/", "vfat", 1, "utf8");
if(ret == 0)
{
fprintf(stdout,"mount() succeeded\n");
sleep(2000);
}
else
fprintf(stdout,"mount() failed: %d, %s\n", errno, strerror(errno));
}
}
Upvotes: 1
Views: 100
Reputation: 11504
Generally speaking, you should never mount same filesystem twice -- if OS drivers will decide to write twice to the same block, you'll get filesystem corruption. Use bind-mounts in such cases.
Linux, however, is smart enough to help you with that -- it will reuse older filesystem mount super_block
(with all mountpoint flags) for a to a location.
I couldn't find it in documentation, but it is traceable through kernel source in sget()
which is called by mount_bdev()
:
hlist_for_each_entry(old, &type->fs_supers, s_instances) {
if (!test(old, data))
continue;
if (!grab_super(old))
goto retry;
if (s) {
up_write(&s->s_umount);
destroy_super(s);
s = NULL;
}
return old;
}
In this snippet it'll seek for previous instance of super_block
corresponding to a block device, and if it already exists -- simply returns it.
Some practical proof using SystemTap:
# stap -e 'probe kernel.function("sget").return {
sb = $return;
active = @cast(sb, "super_block")->s_active->counter;
fsi = @cast(sb, "super_block")->s_fs_info;
uid = fsi == 0 ? -1
: @cast(fsi, "msdos_sb_info", "vfat")->options->fs_uid;
printf("%p active=%d uid=%d\n", sb, active, uid);
}'
Setting uid in second mount doesn't alter option, but increases number of active mounts (obvious):
# mount /dev/sdd1 /tmp/mnt1
0xffff8803ce87e800 active=1 uid=-1
# mount -o uid=1000 /dev/sdd1 /tmp/mnt2
0xffff8803ce87e800 active=2 uid=0
Mounting in reverse order also inherits mount options:
# mount -o uid=1000 /dev/sdd1 /tmp/mnt2
0xffff8803cc609c00 active=1 uid=-1
# mount /dev/sdd1 /tmp/mnt1
0xffff8803cc609c00 active=2 uid=1000
If you wish to know who was responsible for such behavior, ask Linus, similiar code exists since 0.11:
struct super_block * get_super(int dev)
{
struct super_block * s;
if (!dev)
return NULL;
s = 0+super_block;
while (s < NR_SUPER+super_block)
if (s->s_dev == dev) {
wait_on_super(s);
if (s->s_dev == dev)
return s;
s = 0+super_block;
} else
s++;
return NULL;
}
(but when this code was in charge, sys_mount()
explicitly checked that no other mountpoints exist for that superblock).
You can possibly try to ask a question at LKML.
Upvotes: 3