summaryrefslogtreecommitdiffstats
path: root/md/writeup/writing_linux_mount_utility.md
blob: 0959f93cbc5cee4e95b506e8ba337926ec4cb0d6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
title:Writing linux mount utility
keywords:c,linux,kernel,mount

# Writing linux mount utility

## Intro

For long time wanted to understand how all stuff works on system level with linux.
From one side mounting looked like some sort of magic. Decided to write my
own mount utility. As its looks like only one syscall is needed to do that sys_mount.
Other part of it is to see how to get all different file systems and find
options that are possible to pass to mounted partition. All of this also preparations
to write my own linux userspace and to have non-Linux distro. Main inspiration
taken form arsv/minibase source code. 

## Syscalls

To implement mount there is enough just with one syscall mount. Its quite simple use case with just 
passing param to syscall is enough. There is more time spent to figure out with params are supported 
by each of filesystems.

```c
int smount(
    const char *source, 
    const char *target,
    const char *filesystemtype,
    unsigned long mountflags,
    const void *data)
{
    int ret=-1;
    ret = syscall(SYS_mount,source,target,filesystemtype,mountflags,data);
    return ret;
}
```

Comparison to mount utility is straightforward.

#### mount arguments
| Param | Descriptio  |
|---|---|
| source         | Source directory or file to mount |
| target         | Target directory where to mount |
| filesystemtype | filesystem type |
| mountflags     | mount params |
| data           | specific to filesystem |

### Supported filesystems

Run command to find filesystems supported by currently running kernel

```
cat /proc/filesystems
``` 

### Mount flags

Mount flags that can be passed when mounting filesystems, those are taken from kernel definition

```c
#define _MS_RDONLY       (1<<0)
#define _MS_NOSUID       (1<<1)
#define _MS_NODEV        (1<<2)
#define _MS_NOEXEC       (1<<3)
#define _MS_SYNCHRONOUS  (1<<4)
#define _MS_REMOUNT      (1<<5)
#define _MS_MANDLOCK     (1<<6)
#define _MS_DIRSYNC      (1<<7)
#define _MS_NOATIME      (1<<10)
#define _MS_NODIRATIME   (1<<11)
#define _MS_BIND         (1<<12)
#define _MS_MOVE         (1<<13)
#define _MS_REC          (1<<14)
#define _MS_SILENT       (1<<15)
#define _MS_POSIXACL     (1<<16)
#define _MS_UNBINDABLE   (1<<17)
#define _MS_PRIVATE      (1<<18)
#define _MS_SLAVE        (1<<19)
#define _MS_SHARED       (1<<20)
#define _MS_RELATIME     (1<<21)
#define _MS_KERNMOUNT    (1<<22)
#define _MS_I_VERSION    (1<<23)
#define _MS_STRICTATIME  (1<<24)
#define _MS_LAZYTIME     (1<<25)
```



## Kernel implementation

Latest linux kernel source code where mount syscall is define is  
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/namespace.c  

There are 2 definitions one for syscall and one for mount code
```c
SYSCALL_DEFINE5(mount, char user *, dev_name, char user *, dir_name,
		char user *, type, unsigned long, flags, void user *, data)

long do_mount(
	const char *dev_name, 
	const char __user *dir_name,
	const char *type_page, 
	unsigned long flags, 
	void *data_page);
```

## Filesystems

There is always a mystery where all parameters that are supported by particular filesystem comes from.
I spent some time to find places where all params that are supported for procfs, devtmps and tempfs come from.

### Procfs

| Param | Description |
|---|---|
| hidepid | __0__ - Everybody may access all proc, __1__ - User can access only their own proc, not other /proc/[pid], __2__ - As for mode 1, extra hides pid directories of others | 
| gid | usergroup who sees /proc in mode 0 |

## Examples

### Mount proc

```bash
nmount_static -t proc -d ma_proc -f hidepid=0 -v
```

### Mount mqueue

```bash
nmount_static -t mqueue -d ma_quque -f noexec -v
```

### Mount devtmpfs
```bash
mount_static -t devtmpfs -d mq_dev -f noexec -v
```

### Mount tmpfs
```bash
nmount_static -t mqueue -d ma_ram -f noexec -v
```

### Mount sysfs
```bash 
nmount_static -t sysfs -d ma_sys -f silent -v
```


## Links

http://git.main.lv/cgit.cgi/nmount.git/  
https://man7.org/linux/man-pages/man7/signal.7.html  
https://man7.org/linux/man-pages/man2/mount.2.html  
https://github.com/arsv/minibase/blob/master/src/rootfs/kmount.c  
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/fs/namespace.c  
[devtmpfs]  
https://www.kernel.org/doc/Documentation/filesystems/sysfs.txt  
https://elixir.bootlin.com/linux/v4.14.184/source/drivers/base/devtmpfs.c  
https://man7.org/linux/man-pages/man7/file-hierarchy.7.html  
[procsf]  
https://www.man7.org/linux/man-pages/man5/proc.5.html  
https://elixir.bootlin.com/linux/v4.14.184/source/fs/proc/root.c#L33  
https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt