Blame view

fs/statfs.c 9.41 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
7ed1ee611   Al Viro   Take statfs varia...
2
  #include <linux/syscalls.h>
630d9c472   Paul Gortmaker   fs: reduce the us...
3
  #include <linux/export.h>
7ed1ee611   Al Viro   Take statfs varia...
4
5
  #include <linux/fs.h>
  #include <linux/file.h>
365b18189   Christoph Hellwig   add f_flags to st...
6
  #include <linux/mount.h>
7ed1ee611   Al Viro   Take statfs varia...
7
8
9
10
  #include <linux/namei.h>
  #include <linux/statfs.h>
  #include <linux/security.h>
  #include <linux/uaccess.h>
4ada54ee7   Al Viro   statfs: move comp...
11
  #include <linux/compat.h>
cf31e70d6   Al Viro   vfs: new helper -...
12
  #include "internal.h"
7ed1ee611   Al Viro   Take statfs varia...
13

365b18189   Christoph Hellwig   add f_flags to st...
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
  static int flags_by_mnt(int mnt_flags)
  {
  	int flags = 0;
  
  	if (mnt_flags & MNT_READONLY)
  		flags |= ST_RDONLY;
  	if (mnt_flags & MNT_NOSUID)
  		flags |= ST_NOSUID;
  	if (mnt_flags & MNT_NODEV)
  		flags |= ST_NODEV;
  	if (mnt_flags & MNT_NOEXEC)
  		flags |= ST_NOEXEC;
  	if (mnt_flags & MNT_NOATIME)
  		flags |= ST_NOATIME;
  	if (mnt_flags & MNT_NODIRATIME)
  		flags |= ST_NODIRATIME;
  	if (mnt_flags & MNT_RELATIME)
  		flags |= ST_RELATIME;
  	return flags;
  }
  
  static int flags_by_sb(int s_flags)
  {
  	int flags = 0;
  	if (s_flags & MS_SYNCHRONOUS)
  		flags |= ST_SYNCHRONOUS;
  	if (s_flags & MS_MANDLOCK)
  		flags |= ST_MANDLOCK;
a8e2b6367   Carlos Maiolino   Make statfs prope...
42
43
  	if (s_flags & MS_RDONLY)
  		flags |= ST_RDONLY;
365b18189   Christoph Hellwig   add f_flags to st...
44
45
46
47
48
49
50
51
  	return flags;
  }
  
  static int calculate_f_flags(struct vfsmount *mnt)
  {
  	return ST_VALID | flags_by_mnt(mnt->mnt_flags) |
  		flags_by_sb(mnt->mnt_sb->s_flags);
  }
cf31e70d6   Al Viro   vfs: new helper -...
52
  static int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf)
7ed1ee611   Al Viro   Take statfs varia...
53
  {
ebabe9a90   Christoph Hellwig   pass a struct pat...
54
55
56
57
58
59
60
61
62
63
64
65
  	int retval;
  
  	if (!dentry->d_sb->s_op->statfs)
  		return -ENOSYS;
  
  	memset(buf, 0, sizeof(*buf));
  	retval = security_sb_statfs(dentry);
  	if (retval)
  		return retval;
  	retval = dentry->d_sb->s_op->statfs(dentry, buf);
  	if (retval == 0 && buf->f_frsize == 0)
  		buf->f_frsize = buf->f_bsize;
7ed1ee611   Al Viro   Take statfs varia...
66
67
  	return retval;
  }
f0bb5aaf2   Al Viro   vfs: misc struct ...
68
  int vfs_statfs(const struct path *path, struct kstatfs *buf)
ebabe9a90   Christoph Hellwig   pass a struct pat...
69
  {
365b18189   Christoph Hellwig   add f_flags to st...
70
71
72
73
74
75
  	int error;
  
  	error = statfs_by_dentry(path->dentry, buf);
  	if (!error)
  		buf->f_flags = calculate_f_flags(path->mnt);
  	return error;
ebabe9a90   Christoph Hellwig   pass a struct pat...
76
  }
7ed1ee611   Al Viro   Take statfs varia...
77
  EXPORT_SYMBOL(vfs_statfs);
c8b91accf   Al Viro   clean statfs-like...
78
  int user_statfs(const char __user *pathname, struct kstatfs *st)
7ed1ee611   Al Viro   Take statfs varia...
79
  {
c8b91accf   Al Viro   clean statfs-like...
80
  	struct path path;
96948fc60   Jeff Layton   vfs: fix user_sta...
81
82
83
84
  	int error;
  	unsigned int lookup_flags = LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT;
  retry:
  	error = user_path_at(AT_FDCWD, pathname, lookup_flags, &path);
c8b91accf   Al Viro   clean statfs-like...
85
86
87
  	if (!error) {
  		error = vfs_statfs(&path, st);
  		path_put(&path);
96948fc60   Jeff Layton   vfs: fix user_sta...
88
89
90
91
  		if (retry_estale(error, lookup_flags)) {
  			lookup_flags |= LOOKUP_REVAL;
  			goto retry;
  		}
c8b91accf   Al Viro   clean statfs-like...
92
93
94
  	}
  	return error;
  }
7ed1ee611   Al Viro   Take statfs varia...
95

c8b91accf   Al Viro   clean statfs-like...
96
97
  int fd_statfs(int fd, struct kstatfs *st)
  {
9d05746e7   Linus Torvalds   vfs: allow O_PATH...
98
  	struct fd f = fdget_raw(fd);
c8b91accf   Al Viro   clean statfs-like...
99
  	int error = -EBADF;
2903ff019   Al Viro   switch simple cas...
100
101
102
  	if (f.file) {
  		error = vfs_statfs(&f.file->f_path, st);
  		fdput(f);
c8b91accf   Al Viro   clean statfs-like...
103
104
105
  	}
  	return error;
  }
7ed1ee611   Al Viro   Take statfs varia...
106

c8b91accf   Al Viro   clean statfs-like...
107
108
109
110
111
112
  static int do_statfs_native(struct kstatfs *st, struct statfs __user *p)
  {
  	struct statfs buf;
  
  	if (sizeof(buf) == sizeof(*st))
  		memcpy(&buf, st, sizeof(*st));
7ed1ee611   Al Viro   Take statfs varia...
113
  	else {
c8b91accf   Al Viro   clean statfs-like...
114
115
116
  		if (sizeof buf.f_blocks == 4) {
  			if ((st->f_blocks | st->f_bfree | st->f_bavail |
  			     st->f_bsize | st->f_frsize) &
7ed1ee611   Al Viro   Take statfs varia...
117
118
119
120
121
122
  			    0xffffffff00000000ULL)
  				return -EOVERFLOW;
  			/*
  			 * f_files and f_ffree may be -1; it's okay to stuff
  			 * that into 32 bits
  			 */
c8b91accf   Al Viro   clean statfs-like...
123
124
  			if (st->f_files != -1 &&
  			    (st->f_files & 0xffffffff00000000ULL))
7ed1ee611   Al Viro   Take statfs varia...
125
  				return -EOVERFLOW;
c8b91accf   Al Viro   clean statfs-like...
126
127
  			if (st->f_ffree != -1 &&
  			    (st->f_ffree & 0xffffffff00000000ULL))
7ed1ee611   Al Viro   Take statfs varia...
128
129
  				return -EOVERFLOW;
  		}
c8b91accf   Al Viro   clean statfs-like...
130
131
132
133
134
135
136
137
138
139
140
141
  		buf.f_type = st->f_type;
  		buf.f_bsize = st->f_bsize;
  		buf.f_blocks = st->f_blocks;
  		buf.f_bfree = st->f_bfree;
  		buf.f_bavail = st->f_bavail;
  		buf.f_files = st->f_files;
  		buf.f_ffree = st->f_ffree;
  		buf.f_fsid = st->f_fsid;
  		buf.f_namelen = st->f_namelen;
  		buf.f_frsize = st->f_frsize;
  		buf.f_flags = st->f_flags;
  		memset(buf.f_spare, 0, sizeof(buf.f_spare));
7ed1ee611   Al Viro   Take statfs varia...
142
  	}
c8b91accf   Al Viro   clean statfs-like...
143
144
  	if (copy_to_user(p, &buf, sizeof(buf)))
  		return -EFAULT;
7ed1ee611   Al Viro   Take statfs varia...
145
146
  	return 0;
  }
c8b91accf   Al Viro   clean statfs-like...
147
  static int do_statfs64(struct kstatfs *st, struct statfs64 __user *p)
7ed1ee611   Al Viro   Take statfs varia...
148
  {
c8b91accf   Al Viro   clean statfs-like...
149
150
151
  	struct statfs64 buf;
  	if (sizeof(buf) == sizeof(*st))
  		memcpy(&buf, st, sizeof(*st));
7ed1ee611   Al Viro   Take statfs varia...
152
  	else {
c8b91accf   Al Viro   clean statfs-like...
153
154
155
156
157
158
159
160
161
162
163
164
  		buf.f_type = st->f_type;
  		buf.f_bsize = st->f_bsize;
  		buf.f_blocks = st->f_blocks;
  		buf.f_bfree = st->f_bfree;
  		buf.f_bavail = st->f_bavail;
  		buf.f_files = st->f_files;
  		buf.f_ffree = st->f_ffree;
  		buf.f_fsid = st->f_fsid;
  		buf.f_namelen = st->f_namelen;
  		buf.f_frsize = st->f_frsize;
  		buf.f_flags = st->f_flags;
  		memset(buf.f_spare, 0, sizeof(buf.f_spare));
7ed1ee611   Al Viro   Take statfs varia...
165
  	}
c8b91accf   Al Viro   clean statfs-like...
166
167
  	if (copy_to_user(p, &buf, sizeof(buf)))
  		return -EFAULT;
7ed1ee611   Al Viro   Take statfs varia...
168
169
170
171
172
  	return 0;
  }
  
  SYSCALL_DEFINE2(statfs, const char __user *, pathname, struct statfs __user *, buf)
  {
c8b91accf   Al Viro   clean statfs-like...
173
174
175
176
  	struct kstatfs st;
  	int error = user_statfs(pathname, &st);
  	if (!error)
  		error = do_statfs_native(&st, buf);
7ed1ee611   Al Viro   Take statfs varia...
177
178
179
180
181
  	return error;
  }
  
  SYSCALL_DEFINE3(statfs64, const char __user *, pathname, size_t, sz, struct statfs64 __user *, buf)
  {
c8b91accf   Al Viro   clean statfs-like...
182
183
  	struct kstatfs st;
  	int error;
7ed1ee611   Al Viro   Take statfs varia...
184
185
  	if (sz != sizeof(*buf))
  		return -EINVAL;
c8b91accf   Al Viro   clean statfs-like...
186
187
188
  	error = user_statfs(pathname, &st);
  	if (!error)
  		error = do_statfs64(&st, buf);
7ed1ee611   Al Viro   Take statfs varia...
189
190
191
192
193
  	return error;
  }
  
  SYSCALL_DEFINE2(fstatfs, unsigned int, fd, struct statfs __user *, buf)
  {
c8b91accf   Al Viro   clean statfs-like...
194
195
196
197
  	struct kstatfs st;
  	int error = fd_statfs(fd, &st);
  	if (!error)
  		error = do_statfs_native(&st, buf);
7ed1ee611   Al Viro   Take statfs varia...
198
199
200
201
202
  	return error;
  }
  
  SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, size_t, sz, struct statfs64 __user *, buf)
  {
c8b91accf   Al Viro   clean statfs-like...
203
  	struct kstatfs st;
7ed1ee611   Al Viro   Take statfs varia...
204
205
206
207
  	int error;
  
  	if (sz != sizeof(*buf))
  		return -EINVAL;
c8b91accf   Al Viro   clean statfs-like...
208
209
210
  	error = fd_statfs(fd, &st);
  	if (!error)
  		error = do_statfs64(&st, buf);
7ed1ee611   Al Viro   Take statfs varia...
211
212
  	return error;
  }
cf31e70d6   Al Viro   vfs: new helper -...
213
  int vfs_ustat(dev_t dev, struct kstatfs *sbuf)
7ed1ee611   Al Viro   Take statfs varia...
214
  {
cf31e70d6   Al Viro   vfs: new helper -...
215
  	struct super_block *s = user_get_super(dev);
7ed1ee611   Al Viro   Take statfs varia...
216
  	int err;
7ed1ee611   Al Viro   Take statfs varia...
217
218
  	if (!s)
  		return -EINVAL;
cf31e70d6   Al Viro   vfs: new helper -...
219
  	err = statfs_by_dentry(s->s_root, sbuf);
7ed1ee611   Al Viro   Take statfs varia...
220
  	drop_super(s);
cf31e70d6   Al Viro   vfs: new helper -...
221
222
223
224
225
226
227
228
  	return err;
  }
  
  SYSCALL_DEFINE2(ustat, unsigned, dev, struct ustat __user *, ubuf)
  {
  	struct ustat tmp;
  	struct kstatfs sbuf;
  	int err = vfs_ustat(new_decode_dev(dev), &sbuf);
7ed1ee611   Al Viro   Take statfs varia...
229
230
231
232
233
234
235
236
237
  	if (err)
  		return err;
  
  	memset(&tmp,0,sizeof(struct ustat));
  	tmp.f_tfree = sbuf.f_bfree;
  	tmp.f_tinode = sbuf.f_ffree;
  
  	return copy_to_user(ubuf, &tmp, sizeof(struct ustat)) ? -EFAULT : 0;
  }
4ada54ee7   Al Viro   statfs: move comp...
238
239
240
241
  
  #ifdef CONFIG_COMPAT
  static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *kbuf)
  {
ae2a9762d   Al Viro   compat statfs: sw...
242
  	struct compat_statfs buf;
4ada54ee7   Al Viro   statfs: move comp...
243
244
245
246
247
248
249
250
251
252
253
254
255
  	if (sizeof ubuf->f_blocks == 4) {
  		if ((kbuf->f_blocks | kbuf->f_bfree | kbuf->f_bavail |
  		     kbuf->f_bsize | kbuf->f_frsize) & 0xffffffff00000000ULL)
  			return -EOVERFLOW;
  		/* f_files and f_ffree may be -1; it's okay
  		 * to stuff that into 32 bits */
  		if (kbuf->f_files != 0xffffffffffffffffULL
  		 && (kbuf->f_files & 0xffffffff00000000ULL))
  			return -EOVERFLOW;
  		if (kbuf->f_ffree != 0xffffffffffffffffULL
  		 && (kbuf->f_ffree & 0xffffffff00000000ULL))
  			return -EOVERFLOW;
  	}
ae2a9762d   Al Viro   compat statfs: sw...
256
257
258
259
260
261
262
263
264
265
266
267
268
269
  	memset(&buf, 0, sizeof(struct compat_statfs));
  	buf.f_type = kbuf->f_type;
  	buf.f_bsize = kbuf->f_bsize;
  	buf.f_blocks = kbuf->f_blocks;
  	buf.f_bfree = kbuf->f_bfree;
  	buf.f_bavail = kbuf->f_bavail;
  	buf.f_files = kbuf->f_files;
  	buf.f_ffree = kbuf->f_ffree;
  	buf.f_namelen = kbuf->f_namelen;
  	buf.f_fsid.val[0] = kbuf->f_fsid.val[0];
  	buf.f_fsid.val[1] = kbuf->f_fsid.val[1];
  	buf.f_frsize = kbuf->f_frsize;
  	buf.f_flags = kbuf->f_flags;
  	if (copy_to_user(ubuf, &buf, sizeof(struct compat_statfs)))
4ada54ee7   Al Viro   statfs: move comp...
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
  		return -EFAULT;
  	return 0;
  }
  
  /*
   * The following statfs calls are copies of code from fs/statfs.c and
   * should be checked against those from time to time
   */
  COMPAT_SYSCALL_DEFINE2(statfs, const char __user *, pathname, struct compat_statfs __user *, buf)
  {
  	struct kstatfs tmp;
  	int error = user_statfs(pathname, &tmp);
  	if (!error)
  		error = put_compat_statfs(buf, &tmp);
  	return error;
  }
  
  COMPAT_SYSCALL_DEFINE2(fstatfs, unsigned int, fd, struct compat_statfs __user *, buf)
  {
  	struct kstatfs tmp;
  	int error = fd_statfs(fd, &tmp);
  	if (!error)
  		error = put_compat_statfs(buf, &tmp);
  	return error;
  }
  
  static int put_compat_statfs64(struct compat_statfs64 __user *ubuf, struct kstatfs *kbuf)
  {
ae2a9762d   Al Viro   compat statfs: sw...
298
  	struct compat_statfs64 buf;
4ada54ee7   Al Viro   statfs: move comp...
299
300
301
302
303
304
305
306
307
308
309
310
311
  	if (sizeof(ubuf->f_bsize) == 4) {
  		if ((kbuf->f_type | kbuf->f_bsize | kbuf->f_namelen |
  		     kbuf->f_frsize | kbuf->f_flags) & 0xffffffff00000000ULL)
  			return -EOVERFLOW;
  		/* f_files and f_ffree may be -1; it's okay
  		 * to stuff that into 32 bits */
  		if (kbuf->f_files != 0xffffffffffffffffULL
  		 && (kbuf->f_files & 0xffffffff00000000ULL))
  			return -EOVERFLOW;
  		if (kbuf->f_ffree != 0xffffffffffffffffULL
  		 && (kbuf->f_ffree & 0xffffffff00000000ULL))
  			return -EOVERFLOW;
  	}
ae2a9762d   Al Viro   compat statfs: sw...
312
313
314
315
316
317
318
319
320
321
322
323
324
325
  	memset(&buf, 0, sizeof(struct compat_statfs64));
  	buf.f_type = kbuf->f_type;
  	buf.f_bsize = kbuf->f_bsize;
  	buf.f_blocks = kbuf->f_blocks;
  	buf.f_bfree = kbuf->f_bfree;
  	buf.f_bavail = kbuf->f_bavail;
  	buf.f_files = kbuf->f_files;
  	buf.f_ffree = kbuf->f_ffree;
  	buf.f_namelen = kbuf->f_namelen;
  	buf.f_fsid.val[0] = kbuf->f_fsid.val[0];
  	buf.f_fsid.val[1] = kbuf->f_fsid.val[1];
  	buf.f_frsize = kbuf->f_frsize;
  	buf.f_flags = kbuf->f_flags;
  	if (copy_to_user(ubuf, &buf, sizeof(struct compat_statfs64)))
4ada54ee7   Al Viro   statfs: move comp...
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
  		return -EFAULT;
  	return 0;
  }
  
  COMPAT_SYSCALL_DEFINE3(statfs64, const char __user *, pathname, compat_size_t, sz, struct compat_statfs64 __user *, buf)
  {
  	struct kstatfs tmp;
  	int error;
  
  	if (sz != sizeof(*buf))
  		return -EINVAL;
  
  	error = user_statfs(pathname, &tmp);
  	if (!error)
  		error = put_compat_statfs64(buf, &tmp);
  	return error;
  }
  
  COMPAT_SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, compat_size_t, sz, struct compat_statfs64 __user *, buf)
  {
  	struct kstatfs tmp;
  	int error;
  
  	if (sz != sizeof(*buf))
  		return -EINVAL;
  
  	error = fd_statfs(fd, &tmp);
  	if (!error)
  		error = put_compat_statfs64(buf, &tmp);
  	return error;
  }
  
  /*
   * This is a copy of sys_ustat, just dealing with a structure layout.
   * Given how simple this syscall is that apporach is more maintainable
   * than the various conversion hacks.
   */
  COMPAT_SYSCALL_DEFINE2(ustat, unsigned, dev, struct compat_ustat __user *, u)
  {
  	struct compat_ustat tmp;
  	struct kstatfs sbuf;
  	int err = vfs_ustat(new_decode_dev(dev), &sbuf);
  	if (err)
  		return err;
  
  	memset(&tmp, 0, sizeof(struct compat_ustat));
  	tmp.f_tfree = sbuf.f_bfree;
  	tmp.f_tinode = sbuf.f_ffree;
  	if (copy_to_user(u, &tmp, sizeof(struct compat_ustat)))
  		return -EFAULT;
  	return 0;
  }
  #endif