Blame view

fs/statfs.c 5.22 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;
5c8a0fbba   Dan McGee   VFS: fix statfs()...
77
  	int error = user_path_at(AT_FDCWD, pathname, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &path);
c8b91accf   Al Viro   clean statfs-like...
78
79
80
81
82
83
  	if (!error) {
  		error = vfs_statfs(&path, st);
  		path_put(&path);
  	}
  	return error;
  }
7ed1ee611   Al Viro   Take statfs varia...
84

c8b91accf   Al Viro   clean statfs-like...
85
86
  int fd_statfs(int fd, struct kstatfs *st)
  {
0aa2ee5f0   Al Viro   switch statfs to ...
87
88
  	int fput_needed;
  	struct file *file = fget_light(fd, &fput_needed);
c8b91accf   Al Viro   clean statfs-like...
89
90
91
  	int error = -EBADF;
  	if (file) {
  		error = vfs_statfs(&file->f_path, st);
0aa2ee5f0   Al Viro   switch statfs to ...
92
  		fput_light(file, fput_needed);
c8b91accf   Al Viro   clean statfs-like...
93
94
95
  	}
  	return error;
  }
7ed1ee611   Al Viro   Take statfs varia...
96

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