# linux-mount-01-mountflags传参
# mount
mount2 介绍了mount调用的定义
NAME top
mount - mount filesystem
SYNOPSIS top
#include <sys/mount.h>
int mount(const char *source, const char *target,
const char *filesystemtype, unsigned long mountflags,
const void *data);
关键参数有source
,target
,filesystemtype
,mountflags
,data
假设有个分区 /dev/sdb1
, 如果我们要把它挂载到/mnt/test
,并且传参nodev
,noquota
和nosuid
,则可执行如下命令:
mount /dev/sdb1 /mnt/test -o nodev,noquota,nosuid
看下mount结果,可以看到nodev,noquota和nosuid都生效了:
#mount | grep test
/dev/sdb1 on /mnt/test type xfs (rw,nosuid,nodev,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota)
重新mount,通过starce来跟踪看下
# umount /mnt/test
# strace mount /dev/sdb1 /mnt/test -o nodev,noquota,nosuid 2>&1 | grep '/mnt/test'
execve("/usr/bin/mount", ["mount", "/dev/sdb1", "/mnt/test", "-o", "nodev,noquota,nosuid"], 0x7ffc9b571e00 /* 19 vars */) = 0
lstat("/mnt/test", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
mount("/dev/sdb1", "/mnt/test", "xfs", MS_NOSUID|MS_NODEV, "noquota") = 0
可以看到在调用mount方法时,传参为:
mount("/dev/sdb1", "/mnt/test", "xfs", MS_NOSUID|MS_NODEV, "noquota") = 0
这里的MS_NOSUID|MS_NODEV
就是mount参数里的mountflags
,使用了位运算的方式来传参,挺有意思,我们就来看看mountflags
的传参吧。
# 位运算
在开始之前复习下位运算
运算符 | 描述 | 例子 |
---|---|---|
& | 与运算,两值同为1,结果为1,否则为0 | A=0,B=1, A&B=0 |
| | 或运算,只要有一个值为1,结果为1 | A=0,B=1, A|B=1 |
^ | 异或运算,如果a、b两个值相同,结果为0。如果a、b两个值不同,结果为1 | A=0,B=1, A^B=1 |
~ | 取反 | A=1, ~A=-1 |
<< | 左移, 向左移动 | A=1, A<<2=4 |
>> | 右移, 向右移动 | A=3, A>>1=1 |
举个例子,假设有两个数p=23和q=15,对应的二进制为:
p = 10111
q = 1111
p | q 为:
10111 | 01111
每一位一一对应,做|运算,得到
11111
结果为31
我们用代码验证下
>>> p = 23
>>> q = 15
>>> p | q
31
可以看到结果相等。
# mountflags
有了位运算的基础,我们就可以来看下mountflags的传参了,并体会到这样传参的好处。
在/usr/include/linux/mount.h
里可以看到mountflags的定义
/*
* These are the fs-independent mount-flags: up to 32 flags are supported
*
* Usage of these is restricted within the kernel to core mount(2) code and
* callers of sys_mount() only. Filesystems should be using the SB_*
* equivalent instead.
*/
#define MS_RDONLY 1 /* Mount read-only */
#define MS_NOSUID 2 /* Ignore suid and sgid bits */
#define MS_NODEV 4 /* Disallow access to device special files */
#define MS_NOEXEC 8 /* Disallow program execution */
#define MS_SYNCHRONOUS 16 /* Writes are synced at once */
#define MS_REMOUNT 32 /* Alter flags of a mounted FS */
#define MS_MANDLOCK 64 /* Allow mandatory locks on an FS */
#define MS_DIRSYNC 128 /* Directory modifications are synchronous */
#define MS_NOATIME 1024 /* Do not update access times. */
#define MS_NODIRATIME 2048 /* Do not update directory access times */
#define MS_BIND 4096
#define MS_MOVE 8192
#define MS_REC 16384
#define MS_VERBOSE 32768 /* War is peace. Verbosity is silence.
MS_VERBOSE is deprecated. */
#define MS_SILENT 32768
#define MS_POSIXACL (1<<16) /* VFS does not apply the umask */
#define MS_UNBINDABLE (1<<17) /* change to unbindable */
#define MS_PRIVATE (1<<18) /* change to private */
#define MS_SLAVE (1<<19) /* change to slave */
#define MS_SHARED (1<<20) /* change to shared */
#define MS_RELATIME (1<<21) /* Update atime relative to mtime/ctime. */
#define MS_KERNMOUNT (1<<22) /* this is a kern_mount call */
#define MS_I_VERSION (1<<23) /* Update inode I_version field */
#define MS_STRICTATIME (1<<24) /* Always perform atime updates */
#define MS_LAZYTIME (1<<25) /* Update the on-disk [acm]times lazily */
/* These sb flags are internal to the kernel */
#define MS_SUBMOUNT (1<<26)
#define MS_NOREMOTELOCK (1<<27)
#define MS_NOSEC (1<<28)
#define MS_BORN (1<<29)
#define MS_ACTIVE (1<<30)
#define MS_NOUSER (1<<31)
/*
* Superblock flags that can be altered by MS_REMOUNT
*/
#define MS_RMT_MASK (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|\
MS_LAZYTIME)
可以看到里面的参数比较多,如果按照普通的传参方式,我们可能会这样做:
// 定义个函数,并定义几个参数
function mount(MS_RDONLY,MS_NOSUID,MS_NODEV,MS_NOEXEC){
// 逻辑代码里一个个去判断,做计算
if(MS_RDONLY){
// ...
}
if(MS_NOSUID){
// ...
}
if(MS_NODEV){
// ...
}
if(MS_NOEXEC){
// ...
}
}
初看好像没啥问题,但如果参数变多了,且调用函数的地方多了之后,这样的方式就会变得难以维护。 通过看Linux内核代码,我们会发现使用标志位操作就是解决这个问题的一个很好的方式。 看下Linux内核(5.17)里path_mount
/*
* Flags is a 32-bit value that allows up to 31 non-fs dependent flags to
* be given to the mount() call (ie: read-only, no-dev, no-suid etc).
*
* data is a (void *) that can point to any structure up to
* PAGE_SIZE-1 bytes, which can contain arbitrary fs-dependent
* information (or be NULL).
*
* Pre-0.97 versions of mount() didn't have a flags word.
* When the flags word was introduced its top half was required
* to have the magic value 0xC0ED, and this remained so until 2.4.0-test9.
* Therefore, if this magic number is present, it carries no information
* and must be discarded.
*/
int path_mount(const char *dev_name, struct path *path,
const char *type_page, unsigned long flags, void *data_page)
{
unsigned int mnt_flags = 0, sb_flags;
int ret;
/* Discard magic */
if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
flags &= ~MS_MGC_MSK;
/* Basic sanity checks */
if (data_page)
((char *)data_page)[PAGE_SIZE - 1] = 0;
if (flags & MS_NOUSER)
return -EINVAL;
ret = security_sb_mount(dev_name, path, type_page, flags, data_page);
if (ret)
return ret;
if (!may_mount())
return -EPERM;
if (flags & SB_MANDLOCK)
warn_mandlock();
/* Default to relatime unless overriden */
if (!(flags & MS_NOATIME))
mnt_flags |= MNT_RELATIME;
/* Separate the per-mountpoint flags */
if (flags & MS_NOSUID)
mnt_flags |= MNT_NOSUID;
if (flags & MS_NODEV)
mnt_flags |= MNT_NODEV;
if (flags & MS_NOEXEC)
mnt_flags |= MNT_NOEXEC;
if (flags & MS_NOATIME)
mnt_flags |= MNT_NOATIME;
if (flags & MS_NODIRATIME)
mnt_flags |= MNT_NODIRATIME;
if (flags & MS_STRICTATIME)
mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);
if (flags & MS_RDONLY)
mnt_flags |= MNT_READONLY;
if (flags & MS_NOSYMFOLLOW)
mnt_flags |= MNT_NOSYMFOLLOW;
/* The default atime for remount is preservation */
if ((flags & MS_REMOUNT) &&
((flags & (MS_NOATIME | MS_NODIRATIME | MS_RELATIME |
MS_STRICTATIME)) == 0)) {
mnt_flags &= ~MNT_ATIME_MASK;
mnt_flags |= path->mnt->mnt_flags & MNT_ATIME_MASK;
}
sb_flags = flags & (SB_RDONLY |
SB_SYNCHRONOUS |
SB_MANDLOCK |
SB_DIRSYNC |
SB_SILENT |
SB_POSIXACL |
SB_LAZYTIME |
SB_I_VERSION);
if ((flags & (MS_REMOUNT | MS_BIND)) == (MS_REMOUNT | MS_BIND))
return do_reconfigure_mnt(path, mnt_flags);
if (flags & MS_REMOUNT)
return do_remount(path, flags, sb_flags, mnt_flags, data_page);
if (flags & MS_BIND)
return do_loopback(path, dev_name, flags & MS_REC);
if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
return do_change_type(path, flags);
if (flags & MS_MOVE)
return do_move_mount_old(path, dev_name);
return do_new_mount(path, type_page, sb_flags, mnt_flags, dev_name,
data_page);
}
可以看到里面有大量的位运算来判断传参,比如判断MS_NOSUID
参数:
if (flags & MS_NOSUID)
mnt_flags |= MNT_NOSUID;
传参时我们传的是MS_NOSUID|MS_NODEV,所以这里判断MS_NOSUID时实际上是:
(MS_NOSUID|MS_NODEV) & MS_NOSUID
可以说是非常的优雅了。
今天的介绍就到这里了,感谢阅读。