Blame view

fs/statfs.c 5.05 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>
365b18189   Christoph Hellwig   add f_flags to st...
10
11
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
  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);
  }
ebabe9a90   Christoph Hellwig   pass a struct pat...
46
  int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf)
7ed1ee611   Al Viro   Take statfs varia...
47
  {
ebabe9a90   Christoph Hellwig   pass a struct pat...
48
49
50
51
52
53
54
55
56
57
58
59
  	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...
60
61
  	return retval;
  }
ebabe9a90   Christoph Hellwig   pass a struct pat...
62
63
  int vfs_statfs(struct path *path, struct kstatfs *buf)
  {
365b18189   Christoph Hellwig   add f_flags to st...
64
65
66
67
68
69
  	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...
70
  }
7ed1ee611   Al Viro   Take statfs varia...
71
  EXPORT_SYMBOL(vfs_statfs);
c8b91accf   Al Viro   clean statfs-like...
72
  int user_statfs(const char __user *pathname, struct kstatfs *st)
7ed1ee611   Al Viro   Take statfs varia...
73
  {
c8b91accf   Al Viro   clean statfs-like...
74
  	struct path path;
5c8a0fbba   Dan McGee   VFS: fix statfs()...
75
  	int error = user_path_at(AT_FDCWD, pathname, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &path);
c8b91accf   Al Viro   clean statfs-like...
76
77
78
79
80
81
  	if (!error) {
  		error = vfs_statfs(&path, st);
  		path_put(&path);
  	}
  	return error;
  }
7ed1ee611   Al Viro   Take statfs varia...
82

c8b91accf   Al Viro   clean statfs-like...
83
84
85
86
87
88
89
90
91
92
  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...
93

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