]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 LT |
2 | /* |
3 | * linux/fs/stat.c | |
4 | * | |
5 | * Copyright (C) 1991, 1992 Linus Torvalds | |
6 | */ | |
7 | ||
2d985f8c | 8 | #include <linux/blkdev.h> |
630d9c47 | 9 | #include <linux/export.h> |
1da177e4 LT |
10 | #include <linux/mm.h> |
11 | #include <linux/errno.h> | |
12 | #include <linux/file.h> | |
1da177e4 LT |
13 | #include <linux/highuid.h> |
14 | #include <linux/fs.h> | |
15 | #include <linux/namei.h> | |
16 | #include <linux/security.h> | |
5b825c3a | 17 | #include <linux/cred.h> |
1da177e4 | 18 | #include <linux/syscalls.h> |
ba52de12 | 19 | #include <linux/pagemap.h> |
ac565de3 | 20 | #include <linux/compat.h> |
a1175d6b | 21 | #include <linux/iversion.h> |
1da177e4 | 22 | |
7c0f6ba6 | 23 | #include <linux/uaccess.h> |
1da177e4 LT |
24 | #include <asm/unistd.h> |
25 | ||
3934e36f | 26 | #include "internal.h" |
fa2fcf4f | 27 | #include "mount.h" |
3934e36f | 28 | |
a528d35e DH |
29 | /** |
30 | * generic_fillattr - Fill in the basic attributes from the inode struct | |
0d72b928 JL |
31 | * @idmap: idmap of the mount the inode was found from |
32 | * @request_mask: statx request_mask | |
33 | * @inode: Inode to use as the source | |
34 | * @stat: Where to fill in the attributes | |
a528d35e DH |
35 | * |
36 | * Fill in the basic attributes in the kstat structure from data that's to be | |
37 | * found on the VFS inode structure. This is the default if no getattr inode | |
38 | * operation is supplied. | |
0d56a451 | 39 | * |
b74d24f7 CB |
40 | * If the inode has been found through an idmapped mount the idmap of |
41 | * the vfsmount must be passed through @idmap. This function will then | |
42 | * take care to map the inode according to @idmap before filling in the | |
0d56a451 | 43 | * uid and gid filds. On non-idmapped mounts or if permission checking is to be |
376870aa | 44 | * performed on the raw inode simply pass @nop_mnt_idmap. |
a528d35e | 45 | */ |
0d72b928 JL |
46 | void generic_fillattr(struct mnt_idmap *idmap, u32 request_mask, |
47 | struct inode *inode, struct kstat *stat) | |
1da177e4 | 48 | { |
e67fe633 CB |
49 | vfsuid_t vfsuid = i_uid_into_vfsuid(idmap, inode); |
50 | vfsgid_t vfsgid = i_gid_into_vfsgid(idmap, inode); | |
a2bd096f | 51 | |
1da177e4 LT |
52 | stat->dev = inode->i_sb->s_dev; |
53 | stat->ino = inode->i_ino; | |
54 | stat->mode = inode->i_mode; | |
55 | stat->nlink = inode->i_nlink; | |
a2bd096f CB |
56 | stat->uid = vfsuid_into_kuid(vfsuid); |
57 | stat->gid = vfsgid_into_kgid(vfsgid); | |
1da177e4 | 58 | stat->rdev = inode->i_rdev; |
3ddcd056 | 59 | stat->size = i_size_read(inode); |
16a94965 JL |
60 | stat->atime = inode_get_atime(inode); |
61 | stat->mtime = inode_get_mtime(inode); | |
647aa768 | 62 | stat->ctime = inode_get_ctime(inode); |
93407472 | 63 | stat->blksize = i_blocksize(inode); |
3ddcd056 | 64 | stat->blocks = inode->i_blocks; |
0d72b928 JL |
65 | |
66 | if ((request_mask & STATX_CHANGE_COOKIE) && IS_I_VERSION(inode)) { | |
67 | stat->result_mask |= STATX_CHANGE_COOKIE; | |
68 | stat->change_cookie = inode_query_iversion(inode); | |
69 | } | |
70 | ||
a528d35e | 71 | } |
1da177e4 LT |
72 | EXPORT_SYMBOL(generic_fillattr); |
73 | ||
4f911138 AG |
74 | /** |
75 | * generic_fill_statx_attr - Fill in the statx attributes from the inode flags | |
76 | * @inode: Inode to use as the source | |
77 | * @stat: Where to fill in the attribute flags | |
78 | * | |
79 | * Fill in the STATX_ATTR_* flags in the kstat structure for properties of the | |
80 | * inode that are published on i_flags and enforced by the VFS. | |
81 | */ | |
82 | void generic_fill_statx_attr(struct inode *inode, struct kstat *stat) | |
83 | { | |
84 | if (inode->i_flags & S_IMMUTABLE) | |
85 | stat->attributes |= STATX_ATTR_IMMUTABLE; | |
86 | if (inode->i_flags & S_APPEND) | |
87 | stat->attributes |= STATX_ATTR_APPEND; | |
88 | stat->attributes_mask |= KSTAT_ATTR_VFS_FLAGS; | |
89 | } | |
90 | EXPORT_SYMBOL(generic_fill_statx_attr); | |
91 | ||
0f9ca80f PS |
92 | /** |
93 | * generic_fill_statx_atomic_writes - Fill in atomic writes statx attributes | |
94 | * @stat: Where to fill in the attribute flags | |
95 | * @unit_min: Minimum supported atomic write length in bytes | |
96 | * @unit_max: Maximum supported atomic write length in bytes | |
97 | * | |
98 | * Fill in the STATX{_ATTR}_WRITE_ATOMIC flags in the kstat structure from | |
99 | * atomic write unit_min and unit_max values. | |
100 | */ | |
101 | void generic_fill_statx_atomic_writes(struct kstat *stat, | |
102 | unsigned int unit_min, | |
103 | unsigned int unit_max) | |
104 | { | |
105 | /* Confirm that the request type is known */ | |
106 | stat->result_mask |= STATX_WRITE_ATOMIC; | |
107 | ||
108 | /* Confirm that the file attribute type is known */ | |
109 | stat->attributes_mask |= STATX_ATTR_WRITE_ATOMIC; | |
110 | ||
111 | if (unit_min) { | |
112 | stat->atomic_write_unit_min = unit_min; | |
113 | stat->atomic_write_unit_max = unit_max; | |
114 | /* Initially only allow 1x segment */ | |
115 | stat->atomic_write_segments_max = 1; | |
116 | ||
117 | /* Confirm atomic writes are actually supported */ | |
118 | stat->attributes |= STATX_ATTR_WRITE_ATOMIC; | |
119 | } | |
120 | } | |
121 | EXPORT_SYMBOL_GPL(generic_fill_statx_atomic_writes); | |
122 | ||
b7a6ec52 BF |
123 | /** |
124 | * vfs_getattr_nosec - getattr without security checks | |
125 | * @path: file to get attributes from | |
126 | * @stat: structure to return attributes in | |
a528d35e | 127 | * @request_mask: STATX_xxx flags indicating what the caller wants |
f2d077ff | 128 | * @query_flags: Query mode (AT_STATX_SYNC_TYPE) |
b7a6ec52 BF |
129 | * |
130 | * Get attributes without calling security_inode_getattr. | |
131 | * | |
132 | * Currently the only caller other than vfs_getattr is internal to the | |
a528d35e DH |
133 | * filehandle lookup code, which uses only the inode number and returns no |
134 | * attributes to any user. Any other code probably wants vfs_getattr. | |
b7a6ec52 | 135 | */ |
a528d35e DH |
136 | int vfs_getattr_nosec(const struct path *path, struct kstat *stat, |
137 | u32 request_mask, unsigned int query_flags) | |
1da177e4 | 138 | { |
b74d24f7 | 139 | struct mnt_idmap *idmap; |
bb668734 | 140 | struct inode *inode = d_backing_inode(path->dentry); |
1da177e4 | 141 | |
a528d35e DH |
142 | memset(stat, 0, sizeof(*stat)); |
143 | stat->result_mask |= STATX_BASIC_STATS; | |
f2d077ff | 144 | query_flags &= AT_STATX_SYNC_TYPE; |
801e5237 CH |
145 | |
146 | /* allow the fs to override these if it really wants to */ | |
761e28fa MS |
147 | /* SB_NOATIME means filesystem supplies dummy atime value */ |
148 | if (inode->i_sb->s_flags & SB_NOATIME) | |
801e5237 | 149 | stat->result_mask &= ~STATX_ATIME; |
5afa7e8b TT |
150 | |
151 | /* | |
152 | * Note: If you add another clause to set an attribute flag, please | |
153 | * update attributes_mask below. | |
154 | */ | |
801e5237 CH |
155 | if (IS_AUTOMOUNT(inode)) |
156 | stat->attributes |= STATX_ATTR_AUTOMOUNT; | |
157 | ||
712b2698 IW |
158 | if (IS_DAX(inode)) |
159 | stat->attributes |= STATX_ATTR_DAX; | |
160 | ||
5afa7e8b TT |
161 | stat->attributes_mask |= (STATX_ATTR_AUTOMOUNT | |
162 | STATX_ATTR_DAX); | |
163 | ||
b74d24f7 | 164 | idmap = mnt_idmap(path->mnt); |
1da177e4 | 165 | if (inode->i_op->getattr) |
b74d24f7 | 166 | return inode->i_op->getattr(idmap, path, stat, |
8a924db2 SB |
167 | request_mask, |
168 | query_flags | AT_GETATTR_NOSEC); | |
1da177e4 | 169 | |
0d72b928 | 170 | generic_fillattr(idmap, request_mask, inode, stat); |
1da177e4 LT |
171 | return 0; |
172 | } | |
b7a6ec52 BF |
173 | EXPORT_SYMBOL(vfs_getattr_nosec); |
174 | ||
a528d35e DH |
175 | /* |
176 | * vfs_getattr - Get the enhanced basic attributes of a file | |
177 | * @path: The file of interest | |
178 | * @stat: Where to return the statistics | |
179 | * @request_mask: STATX_xxx flags indicating what the caller wants | |
f2d077ff | 180 | * @query_flags: Query mode (AT_STATX_SYNC_TYPE) |
a528d35e DH |
181 | * |
182 | * Ask the filesystem for a file's attributes. The caller must indicate in | |
183 | * request_mask and query_flags to indicate what they want. | |
184 | * | |
185 | * If the file is remote, the filesystem can be forced to update the attributes | |
186 | * from the backing store by passing AT_STATX_FORCE_SYNC in query_flags or can | |
187 | * suppress the update by passing AT_STATX_DONT_SYNC. | |
188 | * | |
189 | * Bits must have been set in request_mask to indicate which attributes the | |
190 | * caller wants retrieving. Any such attribute not requested may be returned | |
191 | * anyway, but the value may be approximate, and, if remote, may not have been | |
192 | * synchronised with the server. | |
193 | * | |
194 | * 0 will be returned on success, and a -ve error code if unsuccessful. | |
195 | */ | |
196 | int vfs_getattr(const struct path *path, struct kstat *stat, | |
197 | u32 request_mask, unsigned int query_flags) | |
b7a6ec52 BF |
198 | { |
199 | int retval; | |
200 | ||
8a924db2 SB |
201 | if (WARN_ON_ONCE(query_flags & AT_GETATTR_NOSEC)) |
202 | return -EPERM; | |
203 | ||
3f7036a0 | 204 | retval = security_inode_getattr(path); |
b7a6ec52 BF |
205 | if (retval) |
206 | return retval; | |
a528d35e | 207 | return vfs_getattr_nosec(path, stat, request_mask, query_flags); |
b7a6ec52 | 208 | } |
1da177e4 LT |
209 | EXPORT_SYMBOL(vfs_getattr); |
210 | ||
a528d35e | 211 | /** |
da9aa5d9 | 212 | * vfs_fstat - Get the basic attributes by file descriptor |
a528d35e DH |
213 | * @fd: The file descriptor referring to the file of interest |
214 | * @stat: The result structure to fill in. | |
a528d35e DH |
215 | * |
216 | * This function is a wrapper around vfs_getattr(). The main difference is | |
217 | * that it uses a file descriptor to determine the file location. | |
218 | * | |
219 | * 0 will be returned on success, and a -ve error code if unsuccessful. | |
220 | */ | |
da9aa5d9 | 221 | int vfs_fstat(int fd, struct kstat *stat) |
1da177e4 | 222 | { |
8c7493aa | 223 | struct fd f; |
da9aa5d9 | 224 | int error; |
8c7493aa EB |
225 | |
226 | f = fdget_raw(fd); | |
da9aa5d9 CH |
227 | if (!f.file) |
228 | return -EBADF; | |
229 | error = vfs_getattr(&f.file->f_path, stat, STATX_BASIC_STATS, 0); | |
230 | fdput(f); | |
1da177e4 LT |
231 | return error; |
232 | } | |
1da177e4 | 233 | |
1b6fe6e0 SR |
234 | int getname_statx_lookup_flags(int flags) |
235 | { | |
236 | int lookup_flags = 0; | |
237 | ||
238 | if (!(flags & AT_SYMLINK_NOFOLLOW)) | |
239 | lookup_flags |= LOOKUP_FOLLOW; | |
240 | if (!(flags & AT_NO_AUTOMOUNT)) | |
241 | lookup_flags |= LOOKUP_AUTOMOUNT; | |
242 | if (flags & AT_EMPTY_PATH) | |
243 | lookup_flags |= LOOKUP_EMPTY; | |
244 | ||
245 | return lookup_flags; | |
246 | } | |
247 | ||
0ef625bb MG |
248 | static int vfs_statx_path(struct path *path, int flags, struct kstat *stat, |
249 | u32 request_mask) | |
250 | { | |
251 | int error = vfs_getattr(path, stat, request_mask, flags); | |
252 | ||
253 | if (request_mask & STATX_MNT_ID_UNIQUE) { | |
254 | stat->mnt_id = real_mount(path->mnt)->mnt_id_unique; | |
255 | stat->result_mask |= STATX_MNT_ID_UNIQUE; | |
256 | } else { | |
257 | stat->mnt_id = real_mount(path->mnt)->mnt_id; | |
258 | stat->result_mask |= STATX_MNT_ID; | |
259 | } | |
260 | ||
261 | if (path_mounted(path)) | |
262 | stat->attributes |= STATX_ATTR_MOUNT_ROOT; | |
263 | stat->attributes_mask |= STATX_ATTR_MOUNT_ROOT; | |
264 | ||
3e781988 LT |
265 | /* |
266 | * If this is a block device inode, override the filesystem | |
267 | * attributes with the block device specific parameters that need to be | |
268 | * obtained from the bdev backing inode. | |
269 | */ | |
270 | if (S_ISBLK(stat->mode)) | |
271 | bdev_statx(path, stat, request_mask); | |
0ef625bb MG |
272 | |
273 | return error; | |
274 | } | |
275 | ||
276 | static int vfs_statx_fd(int fd, int flags, struct kstat *stat, | |
277 | u32 request_mask) | |
278 | { | |
279 | CLASS(fd_raw, f)(fd); | |
280 | if (!f.file) | |
281 | return -EBADF; | |
282 | return vfs_statx_path(&f.file->f_path, flags, stat, request_mask); | |
283 | } | |
284 | ||
a528d35e DH |
285 | /** |
286 | * vfs_statx - Get basic and extra attributes by filename | |
287 | * @dfd: A file descriptor representing the base dir for a relative filename | |
288 | * @filename: The name of the file of interest | |
289 | * @flags: Flags to control the query | |
290 | * @stat: The result structure to fill in. | |
291 | * @request_mask: STATX_xxx flags indicating what the caller wants | |
292 | * | |
293 | * This function is a wrapper around vfs_getattr(). The main difference is | |
294 | * that it uses a filename and base directory to determine the file location. | |
295 | * Additionally, the use of AT_SYMLINK_NOFOLLOW in flags will prevent a symlink | |
296 | * at the given name from being referenced. | |
297 | * | |
a528d35e DH |
298 | * 0 will be returned on success, and a -ve error code if unsuccessful. |
299 | */ | |
1b6fe6e0 | 300 | static int vfs_statx(int dfd, struct filename *filename, int flags, |
a528d35e | 301 | struct kstat *stat, u32 request_mask) |
0112fc22 | 302 | { |
2eae7a18 | 303 | struct path path; |
1b6fe6e0 | 304 | unsigned int lookup_flags = getname_statx_lookup_flags(flags); |
b3f05150 | 305 | int error; |
0112fc22 | 306 | |
b3f05150 | 307 | if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH | |
f2d077ff | 308 | AT_STATX_SYNC_TYPE)) |
a528d35e | 309 | return -EINVAL; |
b3f05150 | 310 | |
836fb7e7 | 311 | retry: |
1b6fe6e0 | 312 | error = filename_lookup(dfd, filename, lookup_flags, &path, NULL); |
2eae7a18 | 313 | if (error) |
0ef625bb MG |
314 | return error; |
315 | error = vfs_statx_path(&path, flags, stat, request_mask); | |
2eae7a18 | 316 | path_put(&path); |
836fb7e7 JL |
317 | if (retry_estale(error, lookup_flags)) { |
318 | lookup_flags |= LOOKUP_REVAL; | |
319 | goto retry; | |
320 | } | |
0112fc22 OD |
321 | return error; |
322 | } | |
2eae7a18 | 323 | |
09f1bde4 CH |
324 | int vfs_fstatat(int dfd, const char __user *filename, |
325 | struct kstat *stat, int flags) | |
326 | { | |
1b6fe6e0 SR |
327 | int ret; |
328 | int statx_flags = flags | AT_NO_AUTOMOUNT; | |
329 | struct filename *name; | |
330 | ||
9013c51c LT |
331 | /* |
332 | * Work around glibc turning fstat() into fstatat(AT_EMPTY_PATH) | |
333 | * | |
334 | * If AT_EMPTY_PATH is set, we expect the common case to be that | |
335 | * empty path, and avoid doing all the extra pathname work. | |
336 | */ | |
27a2d0cb CB |
337 | if (flags == AT_EMPTY_PATH && vfs_empty_path(dfd, filename)) |
338 | return vfs_fstat(dfd, stat); | |
9013c51c | 339 | |
dff60734 | 340 | name = getname_flags(filename, getname_statx_lookup_flags(statx_flags)); |
1b6fe6e0 SR |
341 | ret = vfs_statx(dfd, name, statx_flags, stat, STATX_BASIC_STATS); |
342 | putname(name); | |
343 | ||
344 | return ret; | |
09f1bde4 | 345 | } |
0112fc22 | 346 | |
1da177e4 LT |
347 | #ifdef __ARCH_WANT_OLD_STAT |
348 | ||
349 | /* | |
350 | * For backward compatibility? Maybe this should be moved | |
351 | * into arch/i386 instead? | |
352 | */ | |
353 | static int cp_old_stat(struct kstat *stat, struct __old_kernel_stat __user * statbuf) | |
354 | { | |
355 | static int warncount = 5; | |
356 | struct __old_kernel_stat tmp; | |
a528d35e | 357 | |
1da177e4 LT |
358 | if (warncount > 0) { |
359 | warncount--; | |
360 | printk(KERN_WARNING "VFS: Warning: %s using old stat() call. Recompile your binary.\n", | |
361 | current->comm); | |
362 | } else if (warncount < 0) { | |
363 | /* it's laughable, but... */ | |
364 | warncount = 0; | |
365 | } | |
366 | ||
367 | memset(&tmp, 0, sizeof(struct __old_kernel_stat)); | |
368 | tmp.st_dev = old_encode_dev(stat->dev); | |
369 | tmp.st_ino = stat->ino; | |
afefdbb2 DH |
370 | if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino) |
371 | return -EOVERFLOW; | |
1da177e4 LT |
372 | tmp.st_mode = stat->mode; |
373 | tmp.st_nlink = stat->nlink; | |
374 | if (tmp.st_nlink != stat->nlink) | |
375 | return -EOVERFLOW; | |
a7c1938e EB |
376 | SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid)); |
377 | SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid)); | |
1da177e4 LT |
378 | tmp.st_rdev = old_encode_dev(stat->rdev); |
379 | #if BITS_PER_LONG == 32 | |
380 | if (stat->size > MAX_NON_LFS) | |
381 | return -EOVERFLOW; | |
a528d35e | 382 | #endif |
1da177e4 LT |
383 | tmp.st_size = stat->size; |
384 | tmp.st_atime = stat->atime.tv_sec; | |
385 | tmp.st_mtime = stat->mtime.tv_sec; | |
386 | tmp.st_ctime = stat->ctime.tv_sec; | |
387 | return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; | |
388 | } | |
389 | ||
c7887325 DH |
390 | SYSCALL_DEFINE2(stat, const char __user *, filename, |
391 | struct __old_kernel_stat __user *, statbuf) | |
1da177e4 LT |
392 | { |
393 | struct kstat stat; | |
2eae7a18 | 394 | int error; |
1da177e4 | 395 | |
2eae7a18 CH |
396 | error = vfs_stat(filename, &stat); |
397 | if (error) | |
398 | return error; | |
1da177e4 | 399 | |
2eae7a18 | 400 | return cp_old_stat(&stat, statbuf); |
1da177e4 | 401 | } |
257ac264 | 402 | |
c7887325 DH |
403 | SYSCALL_DEFINE2(lstat, const char __user *, filename, |
404 | struct __old_kernel_stat __user *, statbuf) | |
1da177e4 LT |
405 | { |
406 | struct kstat stat; | |
2eae7a18 | 407 | int error; |
1da177e4 | 408 | |
2eae7a18 CH |
409 | error = vfs_lstat(filename, &stat); |
410 | if (error) | |
411 | return error; | |
1da177e4 | 412 | |
2eae7a18 | 413 | return cp_old_stat(&stat, statbuf); |
1da177e4 | 414 | } |
257ac264 HC |
415 | |
416 | SYSCALL_DEFINE2(fstat, unsigned int, fd, struct __old_kernel_stat __user *, statbuf) | |
1da177e4 LT |
417 | { |
418 | struct kstat stat; | |
419 | int error = vfs_fstat(fd, &stat); | |
420 | ||
421 | if (!error) | |
422 | error = cp_old_stat(&stat, statbuf); | |
423 | ||
424 | return error; | |
425 | } | |
426 | ||
427 | #endif /* __ARCH_WANT_OLD_STAT */ | |
428 | ||
82b355d1 AB |
429 | #ifdef __ARCH_WANT_NEW_STAT |
430 | ||
8529f613 LT |
431 | #ifndef INIT_STRUCT_STAT_PADDING |
432 | # define INIT_STRUCT_STAT_PADDING(st) memset(&st, 0, sizeof(st)) | |
433 | #endif | |
434 | ||
1da177e4 LT |
435 | static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf) |
436 | { | |
437 | struct stat tmp; | |
438 | ||
932aba1e MP |
439 | if (sizeof(tmp.st_dev) < 4 && !old_valid_dev(stat->dev)) |
440 | return -EOVERFLOW; | |
441 | if (sizeof(tmp.st_rdev) < 4 && !old_valid_dev(stat->rdev)) | |
1da177e4 | 442 | return -EOVERFLOW; |
a52dd971 LT |
443 | #if BITS_PER_LONG == 32 |
444 | if (stat->size > MAX_NON_LFS) | |
1da177e4 LT |
445 | return -EOVERFLOW; |
446 | #endif | |
447 | ||
8529f613 | 448 | INIT_STRUCT_STAT_PADDING(tmp); |
932aba1e | 449 | tmp.st_dev = new_encode_dev(stat->dev); |
1da177e4 | 450 | tmp.st_ino = stat->ino; |
afefdbb2 DH |
451 | if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino) |
452 | return -EOVERFLOW; | |
1da177e4 LT |
453 | tmp.st_mode = stat->mode; |
454 | tmp.st_nlink = stat->nlink; | |
455 | if (tmp.st_nlink != stat->nlink) | |
456 | return -EOVERFLOW; | |
a7c1938e EB |
457 | SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid)); |
458 | SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid)); | |
932aba1e | 459 | tmp.st_rdev = new_encode_dev(stat->rdev); |
1da177e4 LT |
460 | tmp.st_size = stat->size; |
461 | tmp.st_atime = stat->atime.tv_sec; | |
462 | tmp.st_mtime = stat->mtime.tv_sec; | |
463 | tmp.st_ctime = stat->ctime.tv_sec; | |
464 | #ifdef STAT_HAVE_NSEC | |
465 | tmp.st_atime_nsec = stat->atime.tv_nsec; | |
466 | tmp.st_mtime_nsec = stat->mtime.tv_nsec; | |
467 | tmp.st_ctime_nsec = stat->ctime.tv_nsec; | |
468 | #endif | |
469 | tmp.st_blocks = stat->blocks; | |
470 | tmp.st_blksize = stat->blksize; | |
471 | return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; | |
472 | } | |
473 | ||
c7887325 DH |
474 | SYSCALL_DEFINE2(newstat, const char __user *, filename, |
475 | struct stat __user *, statbuf) | |
5590ff0d UD |
476 | { |
477 | struct kstat stat; | |
2eae7a18 | 478 | int error = vfs_stat(filename, &stat); |
5590ff0d | 479 | |
2eae7a18 CH |
480 | if (error) |
481 | return error; | |
482 | return cp_new_stat(&stat, statbuf); | |
5590ff0d UD |
483 | } |
484 | ||
c7887325 DH |
485 | SYSCALL_DEFINE2(newlstat, const char __user *, filename, |
486 | struct stat __user *, statbuf) | |
1da177e4 LT |
487 | { |
488 | struct kstat stat; | |
2eae7a18 | 489 | int error; |
1da177e4 | 490 | |
2eae7a18 CH |
491 | error = vfs_lstat(filename, &stat); |
492 | if (error) | |
493 | return error; | |
1da177e4 | 494 | |
2eae7a18 | 495 | return cp_new_stat(&stat, statbuf); |
1da177e4 | 496 | } |
5590ff0d | 497 | |
2833c28a | 498 | #if !defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_SYS_NEWFSTATAT) |
c7887325 | 499 | SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename, |
6559eed8 | 500 | struct stat __user *, statbuf, int, flag) |
1da177e4 LT |
501 | { |
502 | struct kstat stat; | |
0112fc22 | 503 | int error; |
1da177e4 | 504 | |
0112fc22 OD |
505 | error = vfs_fstatat(dfd, filename, &stat, flag); |
506 | if (error) | |
507 | return error; | |
508 | return cp_new_stat(&stat, statbuf); | |
1da177e4 | 509 | } |
cff2b760 | 510 | #endif |
5590ff0d | 511 | |
257ac264 | 512 | SYSCALL_DEFINE2(newfstat, unsigned int, fd, struct stat __user *, statbuf) |
1da177e4 LT |
513 | { |
514 | struct kstat stat; | |
515 | int error = vfs_fstat(fd, &stat); | |
516 | ||
517 | if (!error) | |
518 | error = cp_new_stat(&stat, statbuf); | |
519 | ||
520 | return error; | |
521 | } | |
82b355d1 | 522 | #endif |
1da177e4 | 523 | |
2dae0248 DB |
524 | static int do_readlinkat(int dfd, const char __user *pathname, |
525 | char __user *buf, int bufsiz) | |
1da177e4 | 526 | { |
2d8f3038 | 527 | struct path path; |
969ce92d | 528 | struct filename *name; |
1da177e4 | 529 | int error; |
7955119e | 530 | unsigned int lookup_flags = LOOKUP_EMPTY; |
1da177e4 LT |
531 | |
532 | if (bufsiz <= 0) | |
533 | return -EINVAL; | |
534 | ||
7955119e | 535 | retry: |
dff60734 | 536 | name = getname_flags(pathname, lookup_flags); |
969ce92d MG |
537 | error = filename_lookup(dfd, name, lookup_flags, &path, NULL); |
538 | if (unlikely(error)) { | |
539 | putname(name); | |
540 | return error; | |
541 | } | |
1da177e4 | 542 | |
969ce92d MG |
543 | /* |
544 | * AFS mountpoints allow readlink(2) but are not symlinks | |
545 | */ | |
546 | if (d_is_symlink(path.dentry) || | |
547 | d_backing_inode(path.dentry)->i_op->readlink) { | |
548 | error = security_inode_readlink(path.dentry); | |
549 | if (!error) { | |
550 | touch_atime(&path); | |
551 | error = vfs_readlink(path.dentry, buf, bufsiz); | |
7955119e | 552 | } |
969ce92d MG |
553 | } else { |
554 | error = (name->name[0] == '\0') ? -ENOENT : -EINVAL; | |
555 | } | |
556 | path_put(&path); | |
557 | putname(name); | |
558 | if (retry_estale(error, lookup_flags)) { | |
559 | lookup_flags |= LOOKUP_REVAL; | |
560 | goto retry; | |
1da177e4 LT |
561 | } |
562 | return error; | |
563 | } | |
564 | ||
2dae0248 DB |
565 | SYSCALL_DEFINE4(readlinkat, int, dfd, const char __user *, pathname, |
566 | char __user *, buf, int, bufsiz) | |
567 | { | |
568 | return do_readlinkat(dfd, pathname, buf, bufsiz); | |
569 | } | |
570 | ||
002c8976 HC |
571 | SYSCALL_DEFINE3(readlink, const char __user *, path, char __user *, buf, |
572 | int, bufsiz) | |
5590ff0d | 573 | { |
2dae0248 | 574 | return do_readlinkat(AT_FDCWD, path, buf, bufsiz); |
5590ff0d UD |
575 | } |
576 | ||
1da177e4 LT |
577 | |
578 | /* ---------- LFS-64 ----------- */ | |
0753f70f | 579 | #if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64) |
1da177e4 | 580 | |
8529f613 LT |
581 | #ifndef INIT_STRUCT_STAT64_PADDING |
582 | # define INIT_STRUCT_STAT64_PADDING(st) memset(&st, 0, sizeof(st)) | |
583 | #endif | |
584 | ||
1da177e4 LT |
585 | static long cp_new_stat64(struct kstat *stat, struct stat64 __user *statbuf) |
586 | { | |
587 | struct stat64 tmp; | |
588 | ||
8529f613 | 589 | INIT_STRUCT_STAT64_PADDING(tmp); |
1da177e4 LT |
590 | #ifdef CONFIG_MIPS |
591 | /* mips has weird padding, so we don't get 64 bits there */ | |
1da177e4 LT |
592 | tmp.st_dev = new_encode_dev(stat->dev); |
593 | tmp.st_rdev = new_encode_dev(stat->rdev); | |
594 | #else | |
595 | tmp.st_dev = huge_encode_dev(stat->dev); | |
596 | tmp.st_rdev = huge_encode_dev(stat->rdev); | |
597 | #endif | |
598 | tmp.st_ino = stat->ino; | |
afefdbb2 DH |
599 | if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino) |
600 | return -EOVERFLOW; | |
1da177e4 LT |
601 | #ifdef STAT64_HAS_BROKEN_ST_INO |
602 | tmp.__st_ino = stat->ino; | |
603 | #endif | |
604 | tmp.st_mode = stat->mode; | |
605 | tmp.st_nlink = stat->nlink; | |
a7c1938e EB |
606 | tmp.st_uid = from_kuid_munged(current_user_ns(), stat->uid); |
607 | tmp.st_gid = from_kgid_munged(current_user_ns(), stat->gid); | |
1da177e4 LT |
608 | tmp.st_atime = stat->atime.tv_sec; |
609 | tmp.st_atime_nsec = stat->atime.tv_nsec; | |
610 | tmp.st_mtime = stat->mtime.tv_sec; | |
611 | tmp.st_mtime_nsec = stat->mtime.tv_nsec; | |
612 | tmp.st_ctime = stat->ctime.tv_sec; | |
613 | tmp.st_ctime_nsec = stat->ctime.tv_nsec; | |
614 | tmp.st_size = stat->size; | |
615 | tmp.st_blocks = stat->blocks; | |
616 | tmp.st_blksize = stat->blksize; | |
617 | return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; | |
618 | } | |
619 | ||
c7887325 DH |
620 | SYSCALL_DEFINE2(stat64, const char __user *, filename, |
621 | struct stat64 __user *, statbuf) | |
1da177e4 LT |
622 | { |
623 | struct kstat stat; | |
624 | int error = vfs_stat(filename, &stat); | |
625 | ||
626 | if (!error) | |
627 | error = cp_new_stat64(&stat, statbuf); | |
628 | ||
629 | return error; | |
630 | } | |
257ac264 | 631 | |
c7887325 DH |
632 | SYSCALL_DEFINE2(lstat64, const char __user *, filename, |
633 | struct stat64 __user *, statbuf) | |
1da177e4 LT |
634 | { |
635 | struct kstat stat; | |
636 | int error = vfs_lstat(filename, &stat); | |
637 | ||
638 | if (!error) | |
639 | error = cp_new_stat64(&stat, statbuf); | |
640 | ||
641 | return error; | |
642 | } | |
257ac264 HC |
643 | |
644 | SYSCALL_DEFINE2(fstat64, unsigned long, fd, struct stat64 __user *, statbuf) | |
1da177e4 LT |
645 | { |
646 | struct kstat stat; | |
647 | int error = vfs_fstat(fd, &stat); | |
648 | ||
649 | if (!error) | |
650 | error = cp_new_stat64(&stat, statbuf); | |
651 | ||
652 | return error; | |
653 | } | |
654 | ||
c7887325 | 655 | SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename, |
6559eed8 | 656 | struct stat64 __user *, statbuf, int, flag) |
cff2b760 UD |
657 | { |
658 | struct kstat stat; | |
0112fc22 | 659 | int error; |
cff2b760 | 660 | |
0112fc22 OD |
661 | error = vfs_fstatat(dfd, filename, &stat, flag); |
662 | if (error) | |
663 | return error; | |
664 | return cp_new_stat64(&stat, statbuf); | |
cff2b760 | 665 | } |
0753f70f | 666 | #endif /* __ARCH_WANT_STAT64 || __ARCH_WANT_COMPAT_STAT64 */ |
1da177e4 | 667 | |
6f88cc17 | 668 | static noinline_for_stack int |
64bd7204 | 669 | cp_statx(const struct kstat *stat, struct statx __user *buffer) |
a528d35e | 670 | { |
64bd7204 EB |
671 | struct statx tmp; |
672 | ||
673 | memset(&tmp, 0, sizeof(tmp)); | |
674 | ||
a1175d6b JL |
675 | /* STATX_CHANGE_COOKIE is kernel-only for now */ |
676 | tmp.stx_mask = stat->result_mask & ~STATX_CHANGE_COOKIE; | |
64bd7204 | 677 | tmp.stx_blksize = stat->blksize; |
a1175d6b JL |
678 | /* STATX_ATTR_CHANGE_MONOTONIC is kernel-only for now */ |
679 | tmp.stx_attributes = stat->attributes & ~STATX_ATTR_CHANGE_MONOTONIC; | |
64bd7204 EB |
680 | tmp.stx_nlink = stat->nlink; |
681 | tmp.stx_uid = from_kuid_munged(current_user_ns(), stat->uid); | |
682 | tmp.stx_gid = from_kgid_munged(current_user_ns(), stat->gid); | |
683 | tmp.stx_mode = stat->mode; | |
684 | tmp.stx_ino = stat->ino; | |
685 | tmp.stx_size = stat->size; | |
686 | tmp.stx_blocks = stat->blocks; | |
3209f68b | 687 | tmp.stx_attributes_mask = stat->attributes_mask; |
64bd7204 EB |
688 | tmp.stx_atime.tv_sec = stat->atime.tv_sec; |
689 | tmp.stx_atime.tv_nsec = stat->atime.tv_nsec; | |
690 | tmp.stx_btime.tv_sec = stat->btime.tv_sec; | |
691 | tmp.stx_btime.tv_nsec = stat->btime.tv_nsec; | |
692 | tmp.stx_ctime.tv_sec = stat->ctime.tv_sec; | |
693 | tmp.stx_ctime.tv_nsec = stat->ctime.tv_nsec; | |
694 | tmp.stx_mtime.tv_sec = stat->mtime.tv_sec; | |
695 | tmp.stx_mtime.tv_nsec = stat->mtime.tv_nsec; | |
696 | tmp.stx_rdev_major = MAJOR(stat->rdev); | |
697 | tmp.stx_rdev_minor = MINOR(stat->rdev); | |
698 | tmp.stx_dev_major = MAJOR(stat->dev); | |
699 | tmp.stx_dev_minor = MINOR(stat->dev); | |
fa2fcf4f | 700 | tmp.stx_mnt_id = stat->mnt_id; |
825cf206 EB |
701 | tmp.stx_dio_mem_align = stat->dio_mem_align; |
702 | tmp.stx_dio_offset_align = stat->dio_offset_align; | |
2a82bb02 | 703 | tmp.stx_subvol = stat->subvol; |
0f9ca80f PS |
704 | tmp.stx_atomic_write_unit_min = stat->atomic_write_unit_min; |
705 | tmp.stx_atomic_write_unit_max = stat->atomic_write_unit_max; | |
706 | tmp.stx_atomic_write_segments_max = stat->atomic_write_segments_max; | |
64bd7204 EB |
707 | |
708 | return copy_to_user(buffer, &tmp, sizeof(tmp)) ? -EFAULT : 0; | |
a528d35e DH |
709 | } |
710 | ||
1b6fe6e0 | 711 | int do_statx(int dfd, struct filename *filename, unsigned int flags, |
0018784f BM |
712 | unsigned int mask, struct statx __user *buffer) |
713 | { | |
714 | struct kstat stat; | |
715 | int error; | |
716 | ||
717 | if (mask & STATX__RESERVED) | |
718 | return -EINVAL; | |
719 | if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE) | |
720 | return -EINVAL; | |
721 | ||
0ef625bb MG |
722 | /* |
723 | * STATX_CHANGE_COOKIE is kernel-only for now. Ignore requests | |
a1175d6b JL |
724 | * from userland. |
725 | */ | |
726 | mask &= ~STATX_CHANGE_COOKIE; | |
727 | ||
0018784f BM |
728 | error = vfs_statx(dfd, filename, flags, &stat, mask); |
729 | if (error) | |
730 | return error; | |
731 | ||
732 | return cp_statx(&stat, buffer); | |
733 | } | |
734 | ||
0ef625bb MG |
735 | int do_statx_fd(int fd, unsigned int flags, unsigned int mask, |
736 | struct statx __user *buffer) | |
737 | { | |
738 | struct kstat stat; | |
739 | int error; | |
740 | ||
741 | if (mask & STATX__RESERVED) | |
742 | return -EINVAL; | |
743 | if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE) | |
744 | return -EINVAL; | |
745 | ||
746 | /* | |
747 | * STATX_CHANGE_COOKIE is kernel-only for now. Ignore requests | |
748 | * from userland. | |
749 | */ | |
750 | mask &= ~STATX_CHANGE_COOKIE; | |
751 | ||
752 | error = vfs_statx_fd(fd, flags, &stat, mask); | |
753 | if (error) | |
754 | return error; | |
755 | ||
756 | return cp_statx(&stat, buffer); | |
757 | } | |
758 | ||
a528d35e DH |
759 | /** |
760 | * sys_statx - System call to get enhanced stats | |
761 | * @dfd: Base directory to pathwalk from *or* fd to stat. | |
0ef625bb | 762 | * @filename: File to stat or either NULL or "" with AT_EMPTY_PATH |
a528d35e DH |
763 | * @flags: AT_* flags to control pathwalk. |
764 | * @mask: Parts of statx struct actually required. | |
765 | * @buffer: Result buffer. | |
766 | * | |
1e2f82d1 | 767 | * Note that fstat() can be emulated by setting dfd to the fd of interest, |
0ef625bb MG |
768 | * supplying "" (or preferably NULL) as the filename and setting AT_EMPTY_PATH |
769 | * in the flags. | |
a528d35e DH |
770 | */ |
771 | SYSCALL_DEFINE5(statx, | |
772 | int, dfd, const char __user *, filename, unsigned, flags, | |
773 | unsigned int, mask, | |
774 | struct statx __user *, buffer) | |
775 | { | |
1b6fe6e0 | 776 | int ret; |
0ef625bb | 777 | unsigned lflags; |
1b6fe6e0 SR |
778 | struct filename *name; |
779 | ||
0ef625bb MG |
780 | /* |
781 | * Short-circuit handling of NULL and "" paths. | |
782 | * | |
783 | * For a NULL path we require and accept only the AT_EMPTY_PATH flag | |
784 | * (possibly |'d with AT_STATX flags). | |
785 | * | |
786 | * However, glibc on 32-bit architectures implements fstatat as statx | |
787 | * with the "" pathname and AT_NO_AUTOMOUNT | AT_EMPTY_PATH flags. | |
788 | * Supporting this results in the uglification below. | |
789 | */ | |
790 | lflags = flags & ~(AT_NO_AUTOMOUNT | AT_STATX_SYNC_TYPE); | |
791 | if (lflags == AT_EMPTY_PATH && vfs_empty_path(dfd, filename)) | |
792 | return do_statx_fd(dfd, flags & ~AT_NO_AUTOMOUNT, mask, buffer); | |
793 | ||
dff60734 | 794 | name = getname_flags(filename, getname_statx_lookup_flags(flags)); |
1b6fe6e0 SR |
795 | ret = do_statx(dfd, name, flags, mask, buffer); |
796 | putname(name); | |
797 | ||
798 | return ret; | |
a528d35e DH |
799 | } |
800 | ||
f18ed30d | 801 | #if defined(CONFIG_COMPAT) && defined(__ARCH_WANT_COMPAT_STAT) |
ac565de3 AV |
802 | static int cp_compat_stat(struct kstat *stat, struct compat_stat __user *ubuf) |
803 | { | |
804 | struct compat_stat tmp; | |
805 | ||
932aba1e MP |
806 | if (sizeof(tmp.st_dev) < 4 && !old_valid_dev(stat->dev)) |
807 | return -EOVERFLOW; | |
808 | if (sizeof(tmp.st_rdev) < 4 && !old_valid_dev(stat->rdev)) | |
ac565de3 AV |
809 | return -EOVERFLOW; |
810 | ||
811 | memset(&tmp, 0, sizeof(tmp)); | |
932aba1e | 812 | tmp.st_dev = new_encode_dev(stat->dev); |
ac565de3 AV |
813 | tmp.st_ino = stat->ino; |
814 | if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino) | |
815 | return -EOVERFLOW; | |
816 | tmp.st_mode = stat->mode; | |
817 | tmp.st_nlink = stat->nlink; | |
818 | if (tmp.st_nlink != stat->nlink) | |
819 | return -EOVERFLOW; | |
820 | SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid)); | |
821 | SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid)); | |
932aba1e | 822 | tmp.st_rdev = new_encode_dev(stat->rdev); |
ac565de3 AV |
823 | if ((u64) stat->size > MAX_NON_LFS) |
824 | return -EOVERFLOW; | |
825 | tmp.st_size = stat->size; | |
826 | tmp.st_atime = stat->atime.tv_sec; | |
827 | tmp.st_atime_nsec = stat->atime.tv_nsec; | |
828 | tmp.st_mtime = stat->mtime.tv_sec; | |
829 | tmp.st_mtime_nsec = stat->mtime.tv_nsec; | |
830 | tmp.st_ctime = stat->ctime.tv_sec; | |
831 | tmp.st_ctime_nsec = stat->ctime.tv_nsec; | |
832 | tmp.st_blocks = stat->blocks; | |
833 | tmp.st_blksize = stat->blksize; | |
834 | return copy_to_user(ubuf, &tmp, sizeof(tmp)) ? -EFAULT : 0; | |
835 | } | |
836 | ||
837 | COMPAT_SYSCALL_DEFINE2(newstat, const char __user *, filename, | |
838 | struct compat_stat __user *, statbuf) | |
839 | { | |
840 | struct kstat stat; | |
841 | int error; | |
842 | ||
843 | error = vfs_stat(filename, &stat); | |
844 | if (error) | |
845 | return error; | |
846 | return cp_compat_stat(&stat, statbuf); | |
847 | } | |
848 | ||
849 | COMPAT_SYSCALL_DEFINE2(newlstat, const char __user *, filename, | |
850 | struct compat_stat __user *, statbuf) | |
851 | { | |
852 | struct kstat stat; | |
853 | int error; | |
854 | ||
855 | error = vfs_lstat(filename, &stat); | |
856 | if (error) | |
857 | return error; | |
858 | return cp_compat_stat(&stat, statbuf); | |
859 | } | |
860 | ||
861 | #ifndef __ARCH_WANT_STAT64 | |
862 | COMPAT_SYSCALL_DEFINE4(newfstatat, unsigned int, dfd, | |
863 | const char __user *, filename, | |
864 | struct compat_stat __user *, statbuf, int, flag) | |
865 | { | |
866 | struct kstat stat; | |
867 | int error; | |
868 | ||
869 | error = vfs_fstatat(dfd, filename, &stat, flag); | |
870 | if (error) | |
871 | return error; | |
872 | return cp_compat_stat(&stat, statbuf); | |
873 | } | |
874 | #endif | |
875 | ||
876 | COMPAT_SYSCALL_DEFINE2(newfstat, unsigned int, fd, | |
877 | struct compat_stat __user *, statbuf) | |
878 | { | |
879 | struct kstat stat; | |
880 | int error = vfs_fstat(fd, &stat); | |
881 | ||
882 | if (!error) | |
883 | error = cp_compat_stat(&stat, statbuf); | |
884 | return error; | |
885 | } | |
886 | #endif | |
887 | ||
b462707e DM |
888 | /* Caller is here responsible for sufficient locking (ie. inode->i_lock) */ |
889 | void __inode_add_bytes(struct inode *inode, loff_t bytes) | |
1da177e4 | 890 | { |
1da177e4 LT |
891 | inode->i_blocks += bytes >> 9; |
892 | bytes &= 511; | |
893 | inode->i_bytes += bytes; | |
894 | if (inode->i_bytes >= 512) { | |
895 | inode->i_blocks++; | |
896 | inode->i_bytes -= 512; | |
897 | } | |
b462707e | 898 | } |
eb315d2a | 899 | EXPORT_SYMBOL(__inode_add_bytes); |
b462707e DM |
900 | |
901 | void inode_add_bytes(struct inode *inode, loff_t bytes) | |
902 | { | |
903 | spin_lock(&inode->i_lock); | |
904 | __inode_add_bytes(inode, bytes); | |
1da177e4 LT |
905 | spin_unlock(&inode->i_lock); |
906 | } | |
907 | ||
908 | EXPORT_SYMBOL(inode_add_bytes); | |
909 | ||
1c8924eb | 910 | void __inode_sub_bytes(struct inode *inode, loff_t bytes) |
1da177e4 | 911 | { |
1da177e4 LT |
912 | inode->i_blocks -= bytes >> 9; |
913 | bytes &= 511; | |
914 | if (inode->i_bytes < bytes) { | |
915 | inode->i_blocks--; | |
916 | inode->i_bytes += 512; | |
917 | } | |
918 | inode->i_bytes -= bytes; | |
1c8924eb JK |
919 | } |
920 | ||
921 | EXPORT_SYMBOL(__inode_sub_bytes); | |
922 | ||
923 | void inode_sub_bytes(struct inode *inode, loff_t bytes) | |
924 | { | |
925 | spin_lock(&inode->i_lock); | |
926 | __inode_sub_bytes(inode, bytes); | |
1da177e4 LT |
927 | spin_unlock(&inode->i_lock); |
928 | } | |
929 | ||
930 | EXPORT_SYMBOL(inode_sub_bytes); | |
931 | ||
932 | loff_t inode_get_bytes(struct inode *inode) | |
933 | { | |
934 | loff_t ret; | |
935 | ||
936 | spin_lock(&inode->i_lock); | |
f4a8116a | 937 | ret = __inode_get_bytes(inode); |
1da177e4 LT |
938 | spin_unlock(&inode->i_lock); |
939 | return ret; | |
940 | } | |
941 | ||
942 | EXPORT_SYMBOL(inode_get_bytes); | |
943 | ||
944 | void inode_set_bytes(struct inode *inode, loff_t bytes) | |
945 | { | |
946 | /* Caller is here responsible for sufficient locking | |
947 | * (ie. inode->i_lock) */ | |
948 | inode->i_blocks = bytes >> 9; | |
949 | inode->i_bytes = bytes & 511; | |
950 | } | |
951 | ||
952 | EXPORT_SYMBOL(inode_set_bytes); |