Blame view

fs/statfs.c 5.32 KB
7ed1ee611   Al Viro   Take statfs varia...
1
  #include <linux/syscalls.h>
630d9c472   Paul Gortmaker   fs: reduce the us...
2
  #include <linux/export.h>
7ed1ee611   Al Viro   Take statfs varia...
3
4
  #include <linux/fs.h>
  #include <linux/file.h>
365b18189   Christoph Hellwig   add f_flags to st...
5
  #include <linux/mount.h>
7ed1ee611   Al Viro   Take statfs varia...
6
7
8
9
  #include <linux/namei.h>
  #include <linux/statfs.h>
  #include <linux/security.h>
  #include <linux/uaccess.h>
cf31e70d6   Al Viro   vfs: new helper -...
10
  #include "internal.h"
7ed1ee611   Al Viro   Take statfs varia...
11

365b18189   Christoph Hellwig   add f_flags to st...
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
  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;
  	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 -...
48
  static int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf)
7ed1ee611   Al Viro   Take statfs varia...
49
  {
ebabe9a90   Christoph Hellwig   pass a struct pat...
50
51
52
53
54
55
56
57
58
59
60
61
  	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...
62
63
  	return retval;
  }
ebabe9a90   Christoph Hellwig   pass a struct pat...
64
65
  int vfs_statfs(struct path *path, struct kstatfs *buf)
  {
365b18189   Christoph Hellwig   add f_flags to st...
66
67
68
69
70
71
  	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...
72
  }
7ed1ee611   Al Viro   Take statfs varia...
73
  EXPORT_SYMBOL(vfs_statfs);
c8b91accf   Al Viro   clean statfs-like...
74
  int user_statfs(const char __user *pathname, struct kstatfs *st)
7ed1ee611   Al Viro   Take statfs varia...
75
  {
c8b91accf   Al Viro   clean statfs-like...
76
  	struct path path;
96948fc60   Jeff Layton   vfs: fix user_sta...
77
78
79
80
  	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...
81
82
83
  	if (!error) {
  		error = vfs_statfs(&path, st);
  		path_put(&path);
96948fc60   Jeff Layton   vfs: fix user_sta...
84
85
86
87
  		if (retry_estale(error, lookup_flags)) {
  			lookup_flags |= LOOKUP_REVAL;
  			goto retry;
  		}
c8b91accf   Al Viro   clean statfs-like...
88
89
90
  	}
  	return error;
  }
7ed1ee611   Al Viro   Take statfs varia...
91

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

c8b91accf   Al Viro   clean statfs-like...
103
104
105
106
107
108
  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...
109
  	else {
c8b91accf   Al Viro   clean statfs-like...
110
111
112
  		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...
113
114
115
116
117
118
  			    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...
119
120
  			if (st->f_files != -1 &&
  			    (st->f_files & 0xffffffff00000000ULL))
7ed1ee611   Al Viro   Take statfs varia...
121
  				return -EOVERFLOW;
c8b91accf   Al Viro   clean statfs-like...
122
123
  			if (st->f_ffree != -1 &&
  			    (st->f_ffree & 0xffffffff00000000ULL))
7ed1ee611   Al Viro   Take statfs varia...
124
125
  				return -EOVERFLOW;
  		}
c8b91accf   Al Viro   clean statfs-like...
126
127
128
129
130
131
132
133
134
135
136
137
  		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...
138
  	}
c8b91accf   Al Viro   clean statfs-like...
139
140
  	if (copy_to_user(p, &buf, sizeof(buf)))
  		return -EFAULT;
7ed1ee611   Al Viro   Take statfs varia...
141
142
  	return 0;
  }
c8b91accf   Al Viro   clean statfs-like...
143
  static int do_statfs64(struct kstatfs *st, struct statfs64 __user *p)
7ed1ee611   Al Viro   Take statfs varia...
144
  {
c8b91accf   Al Viro   clean statfs-like...
145
146
147
  	struct statfs64 buf;
  	if (sizeof(buf) == sizeof(*st))
  		memcpy(&buf, st, sizeof(*st));
7ed1ee611   Al Viro   Take statfs varia...
148
  	else {
c8b91accf   Al Viro   clean statfs-like...
149
150
151
152
153
154
155
156
157
158
159
160
  		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...
161
  	}
c8b91accf   Al Viro   clean statfs-like...
162
163
  	if (copy_to_user(p, &buf, sizeof(buf)))
  		return -EFAULT;
7ed1ee611   Al Viro   Take statfs varia...
164
165
166
167
168
  	return 0;
  }
  
  SYSCALL_DEFINE2(statfs, const char __user *, pathname, struct statfs __user *, buf)
  {
c8b91accf   Al Viro   clean statfs-like...
169
170
171
172
  	struct kstatfs st;
  	int error = user_statfs(pathname, &st);
  	if (!error)
  		error = do_statfs_native(&st, buf);
7ed1ee611   Al Viro   Take statfs varia...
173
174
175
176
177
  	return error;
  }
  
  SYSCALL_DEFINE3(statfs64, const char __user *, pathname, size_t, sz, struct statfs64 __user *, buf)
  {
c8b91accf   Al Viro   clean statfs-like...
178
179
  	struct kstatfs st;
  	int error;
7ed1ee611   Al Viro   Take statfs varia...
180
181
  	if (sz != sizeof(*buf))
  		return -EINVAL;
c8b91accf   Al Viro   clean statfs-like...
182
183
184
  	error = user_statfs(pathname, &st);
  	if (!error)
  		error = do_statfs64(&st, buf);
7ed1ee611   Al Viro   Take statfs varia...
185
186
187
188
189
  	return error;
  }
  
  SYSCALL_DEFINE2(fstatfs, unsigned int, fd, struct statfs __user *, buf)
  {
c8b91accf   Al Viro   clean statfs-like...
190
191
192
193
  	struct kstatfs st;
  	int error = fd_statfs(fd, &st);
  	if (!error)
  		error = do_statfs_native(&st, buf);
7ed1ee611   Al Viro   Take statfs varia...
194
195
196
197
198
  	return error;
  }
  
  SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, size_t, sz, struct statfs64 __user *, buf)
  {
c8b91accf   Al Viro   clean statfs-like...
199
  	struct kstatfs st;
7ed1ee611   Al Viro   Take statfs varia...
200
201
202
203
  	int error;
  
  	if (sz != sizeof(*buf))
  		return -EINVAL;
c8b91accf   Al Viro   clean statfs-like...
204
205
206
  	error = fd_statfs(fd, &st);
  	if (!error)
  		error = do_statfs64(&st, buf);
7ed1ee611   Al Viro   Take statfs varia...
207
208
  	return error;
  }
cf31e70d6   Al Viro   vfs: new helper -...
209
  int vfs_ustat(dev_t dev, struct kstatfs *sbuf)
7ed1ee611   Al Viro   Take statfs varia...
210
  {
cf31e70d6   Al Viro   vfs: new helper -...
211
  	struct super_block *s = user_get_super(dev);
7ed1ee611   Al Viro   Take statfs varia...
212
  	int err;
7ed1ee611   Al Viro   Take statfs varia...
213
214
  	if (!s)
  		return -EINVAL;
cf31e70d6   Al Viro   vfs: new helper -...
215
  	err = statfs_by_dentry(s->s_root, sbuf);
7ed1ee611   Al Viro   Take statfs varia...
216
  	drop_super(s);
cf31e70d6   Al Viro   vfs: new helper -...
217
218
219
220
221
222
223
224
  	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...
225
226
227
228
229
230
231
232
233
  	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;
  }