Blame view

fs/statfs.c 5.17 KB
7ed1ee611   Al Viro   Take statfs varia...
1
2
3
4
  #include <linux/syscalls.h>
  #include <linux/module.h>
  #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
87
88
89
90
91
92
93
94
  int fd_statfs(int fd, struct kstatfs *st)
  {
  	struct file *file = fget(fd);
  	int error = -EBADF;
  	if (file) {
  		error = vfs_statfs(&file->f_path, st);
  		fput(file);
  	}
  	return error;
  }
7ed1ee611   Al Viro   Take statfs varia...
95

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