Blame view

fs/statfs.c 9.55 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
  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;
1751e8a6c   Linus Torvalds   Rename superblock...
38
  	if (s_flags & SB_SYNCHRONOUS)
365b18189   Christoph Hellwig   add f_flags to st...
39
  		flags |= ST_SYNCHRONOUS;
1751e8a6c   Linus Torvalds   Rename superblock...
40
  	if (s_flags & SB_MANDLOCK)
365b18189   Christoph Hellwig   add f_flags to st...
41
  		flags |= ST_MANDLOCK;
1751e8a6c   Linus Torvalds   Rename superblock...
42
  	if (s_flags & SB_RDONLY)
a8e2b6367   Carlos Maiolino   Make statfs prope...
43
  		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;
  }
ec86ff568   Amir Goldstein   vfs: add vfs_get_...
68
69
70
71
72
73
74
75
76
77
78
79
80
  int vfs_get_fsid(struct dentry *dentry, __kernel_fsid_t *fsid)
  {
  	struct kstatfs st;
  	int error;
  
  	error = statfs_by_dentry(dentry, &st);
  	if (error)
  		return error;
  
  	*fsid = st.f_fsid;
  	return 0;
  }
  EXPORT_SYMBOL(vfs_get_fsid);
f0bb5aaf2   Al Viro   vfs: misc struct ...
81
  int vfs_statfs(const struct path *path, struct kstatfs *buf)
ebabe9a90   Christoph Hellwig   pass a struct pat...
82
  {
365b18189   Christoph Hellwig   add f_flags to st...
83
84
85
86
87
88
  	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...
89
  }
7ed1ee611   Al Viro   Take statfs varia...
90
  EXPORT_SYMBOL(vfs_statfs);
c8b91accf   Al Viro   clean statfs-like...
91
  int user_statfs(const char __user *pathname, struct kstatfs *st)
7ed1ee611   Al Viro   Take statfs varia...
92
  {
c8b91accf   Al Viro   clean statfs-like...
93
  	struct path path;
96948fc60   Jeff Layton   vfs: fix user_sta...
94
95
96
97
  	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...
98
99
100
  	if (!error) {
  		error = vfs_statfs(&path, st);
  		path_put(&path);
96948fc60   Jeff Layton   vfs: fix user_sta...
101
102
103
104
  		if (retry_estale(error, lookup_flags)) {
  			lookup_flags |= LOOKUP_REVAL;
  			goto retry;
  		}
c8b91accf   Al Viro   clean statfs-like...
105
106
107
  	}
  	return error;
  }
7ed1ee611   Al Viro   Take statfs varia...
108

c8b91accf   Al Viro   clean statfs-like...
109
110
  int fd_statfs(int fd, struct kstatfs *st)
  {
9d05746e7   Linus Torvalds   vfs: allow O_PATH...
111
  	struct fd f = fdget_raw(fd);
c8b91accf   Al Viro   clean statfs-like...
112
  	int error = -EBADF;
2903ff019   Al Viro   switch simple cas...
113
114
115
  	if (f.file) {
  		error = vfs_statfs(&f.file->f_path, st);
  		fdput(f);
c8b91accf   Al Viro   clean statfs-like...
116
117
118
  	}
  	return error;
  }
7ed1ee611   Al Viro   Take statfs varia...
119

c8b91accf   Al Viro   clean statfs-like...
120
121
122
123
124
125
  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...
126
  	else {
c8b91accf   Al Viro   clean statfs-like...
127
128
129
  		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...
130
131
132
133
134
135
  			    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...
136
137
  			if (st->f_files != -1 &&
  			    (st->f_files & 0xffffffff00000000ULL))
7ed1ee611   Al Viro   Take statfs varia...
138
  				return -EOVERFLOW;
c8b91accf   Al Viro   clean statfs-like...
139
140
  			if (st->f_ffree != -1 &&
  			    (st->f_ffree & 0xffffffff00000000ULL))
7ed1ee611   Al Viro   Take statfs varia...
141
142
  				return -EOVERFLOW;
  		}
c8b91accf   Al Viro   clean statfs-like...
143
144
145
146
147
148
149
150
151
152
153
154
  		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...
155
  	}
c8b91accf   Al Viro   clean statfs-like...
156
157
  	if (copy_to_user(p, &buf, sizeof(buf)))
  		return -EFAULT;
7ed1ee611   Al Viro   Take statfs varia...
158
159
  	return 0;
  }
c8b91accf   Al Viro   clean statfs-like...
160
  static int do_statfs64(struct kstatfs *st, struct statfs64 __user *p)
7ed1ee611   Al Viro   Take statfs varia...
161
  {
c8b91accf   Al Viro   clean statfs-like...
162
163
164
  	struct statfs64 buf;
  	if (sizeof(buf) == sizeof(*st))
  		memcpy(&buf, st, sizeof(*st));
7ed1ee611   Al Viro   Take statfs varia...
165
  	else {
c8b91accf   Al Viro   clean statfs-like...
166
167
168
169
170
171
172
173
174
175
176
177
  		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...
178
  	}
c8b91accf   Al Viro   clean statfs-like...
179
180
  	if (copy_to_user(p, &buf, sizeof(buf)))
  		return -EFAULT;
7ed1ee611   Al Viro   Take statfs varia...
181
182
183
184
185
  	return 0;
  }
  
  SYSCALL_DEFINE2(statfs, const char __user *, pathname, struct statfs __user *, buf)
  {
c8b91accf   Al Viro   clean statfs-like...
186
187
188
189
  	struct kstatfs st;
  	int error = user_statfs(pathname, &st);
  	if (!error)
  		error = do_statfs_native(&st, buf);
7ed1ee611   Al Viro   Take statfs varia...
190
191
192
193
194
  	return error;
  }
  
  SYSCALL_DEFINE3(statfs64, const char __user *, pathname, size_t, sz, struct statfs64 __user *, buf)
  {
c8b91accf   Al Viro   clean statfs-like...
195
196
  	struct kstatfs st;
  	int error;
7ed1ee611   Al Viro   Take statfs varia...
197
198
  	if (sz != sizeof(*buf))
  		return -EINVAL;
c8b91accf   Al Viro   clean statfs-like...
199
200
201
  	error = user_statfs(pathname, &st);
  	if (!error)
  		error = do_statfs64(&st, buf);
7ed1ee611   Al Viro   Take statfs varia...
202
203
204
205
206
  	return error;
  }
  
  SYSCALL_DEFINE2(fstatfs, unsigned int, fd, struct statfs __user *, buf)
  {
c8b91accf   Al Viro   clean statfs-like...
207
208
209
210
  	struct kstatfs st;
  	int error = fd_statfs(fd, &st);
  	if (!error)
  		error = do_statfs_native(&st, buf);
7ed1ee611   Al Viro   Take statfs varia...
211
212
213
214
215
  	return error;
  }
  
  SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, size_t, sz, struct statfs64 __user *, buf)
  {
c8b91accf   Al Viro   clean statfs-like...
216
  	struct kstatfs st;
7ed1ee611   Al Viro   Take statfs varia...
217
218
219
220
  	int error;
  
  	if (sz != sizeof(*buf))
  		return -EINVAL;
c8b91accf   Al Viro   clean statfs-like...
221
222
223
  	error = fd_statfs(fd, &st);
  	if (!error)
  		error = do_statfs64(&st, buf);
7ed1ee611   Al Viro   Take statfs varia...
224
225
  	return error;
  }
53fd88ab6   Al Viro   make vfs_ustat() ...
226
  static int vfs_ustat(dev_t dev, struct kstatfs *sbuf)
7ed1ee611   Al Viro   Take statfs varia...
227
  {
cf31e70d6   Al Viro   vfs: new helper -...
228
  	struct super_block *s = user_get_super(dev);
7ed1ee611   Al Viro   Take statfs varia...
229
  	int err;
7ed1ee611   Al Viro   Take statfs varia...
230
231
  	if (!s)
  		return -EINVAL;
cf31e70d6   Al Viro   vfs: new helper -...
232
  	err = statfs_by_dentry(s->s_root, sbuf);
7ed1ee611   Al Viro   Take statfs varia...
233
  	drop_super(s);
cf31e70d6   Al Viro   vfs: new helper -...
234
235
236
237
238
239
240
241
  	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...
242
243
244
245
246
247
248
249
250
  	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...
251
252
253
254
  
  #ifdef CONFIG_COMPAT
  static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *kbuf)
  {
ae2a9762d   Al Viro   compat statfs: sw...
255
  	struct compat_statfs buf;
4ada54ee7   Al Viro   statfs: move comp...
256
257
258
259
260
261
262
263
264
265
266
267
268
  	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...
269
270
271
272
273
274
275
276
277
278
279
280
281
282
  	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...
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
  		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...
311
  	struct compat_statfs64 buf;
cc3a7bfe6   Eric Sandeen   vfs: Fix EOVERFLO...
312
313
314
  
  	if ((kbuf->f_bsize | kbuf->f_frsize) & 0xffffffff00000000ULL)
  		return -EOVERFLOW;
ae2a9762d   Al Viro   compat statfs: sw...
315
316
317
318
319
320
321
322
323
324
325
326
327
328
  	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...
329
330
331
  		return -EFAULT;
  	return 0;
  }
9b54bf9d6   Mark Rutland   kernel: add kcomp...
332
  int kcompat_sys_statfs64(const char __user * pathname, compat_size_t sz, struct compat_statfs64 __user * buf)
4ada54ee7   Al Viro   statfs: move comp...
333
334
335
336
337
338
339
340
341
342
343
344
  {
  	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;
  }
9b54bf9d6   Mark Rutland   kernel: add kcomp...
345
346
347
348
349
350
  COMPAT_SYSCALL_DEFINE3(statfs64, const char __user *, pathname, compat_size_t, sz, struct compat_statfs64 __user *, buf)
  {
  	return kcompat_sys_statfs64(pathname, sz, buf);
  }
  
  int kcompat_sys_fstatfs64(unsigned int fd, compat_size_t sz, struct compat_statfs64 __user * buf)
4ada54ee7   Al Viro   statfs: move comp...
351
352
353
354
355
356
357
358
359
360
361
362
  {
  	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;
  }
9b54bf9d6   Mark Rutland   kernel: add kcomp...
363
364
365
366
  COMPAT_SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, compat_size_t, sz, struct compat_statfs64 __user *, buf)
  {
  	return kcompat_sys_fstatfs64(fd, sz, buf);
  }
4ada54ee7   Al Viro   statfs: move comp...
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
  /*
   * 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