Blame view

fs/statfs.c 9.61 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
  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;
dab741e0e   Mattias Nissler   Add a "nosymfollo...
32
33
  	if (mnt_flags & MNT_NOSYMFOLLOW)
  		flags |= ST_NOSYMFOLLOW;
365b18189   Christoph Hellwig   add f_flags to st...
34
35
36
37
38
39
  	return flags;
  }
  
  static int flags_by_sb(int s_flags)
  {
  	int flags = 0;
1751e8a6c   Linus Torvalds   Rename superblock...
40
  	if (s_flags & SB_SYNCHRONOUS)
365b18189   Christoph Hellwig   add f_flags to st...
41
  		flags |= ST_SYNCHRONOUS;
1751e8a6c   Linus Torvalds   Rename superblock...
42
  	if (s_flags & SB_MANDLOCK)
365b18189   Christoph Hellwig   add f_flags to st...
43
  		flags |= ST_MANDLOCK;
1751e8a6c   Linus Torvalds   Rename superblock...
44
  	if (s_flags & SB_RDONLY)
a8e2b6367   Carlos Maiolino   Make statfs prope...
45
  		flags |= ST_RDONLY;
365b18189   Christoph Hellwig   add f_flags to st...
46
47
48
49
50
51
52
53
  	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 -...
54
  static int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf)
7ed1ee611   Al Viro   Take statfs varia...
55
  {
ebabe9a90   Christoph Hellwig   pass a struct pat...
56
57
58
59
60
61
62
63
64
65
66
67
  	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...
68
69
  	return retval;
  }
ec86ff568   Amir Goldstein   vfs: add vfs_get_...
70
71
72
73
74
75
76
77
78
79
80
81
82
  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 ...
83
  int vfs_statfs(const struct path *path, struct kstatfs *buf)
ebabe9a90   Christoph Hellwig   pass a struct pat...
84
  {
365b18189   Christoph Hellwig   add f_flags to st...
85
86
87
88
89
90
  	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...
91
  }
7ed1ee611   Al Viro   Take statfs varia...
92
  EXPORT_SYMBOL(vfs_statfs);
c8b91accf   Al Viro   clean statfs-like...
93
  int user_statfs(const char __user *pathname, struct kstatfs *st)
7ed1ee611   Al Viro   Take statfs varia...
94
  {
c8b91accf   Al Viro   clean statfs-like...
95
  	struct path path;
96948fc60   Jeff Layton   vfs: fix user_sta...
96
97
98
99
  	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...
100
101
102
  	if (!error) {
  		error = vfs_statfs(&path, st);
  		path_put(&path);
96948fc60   Jeff Layton   vfs: fix user_sta...
103
104
105
106
  		if (retry_estale(error, lookup_flags)) {
  			lookup_flags |= LOOKUP_REVAL;
  			goto retry;
  		}
c8b91accf   Al Viro   clean statfs-like...
107
108
109
  	}
  	return error;
  }
7ed1ee611   Al Viro   Take statfs varia...
110

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

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