| .. | .. |
|---|
| 21 | 21 | #include <linux/uaccess.h> |
|---|
| 22 | 22 | #include <asm/unistd.h> |
|---|
| 23 | 23 | |
|---|
| 24 | +#include "internal.h" |
|---|
| 25 | +#include "mount.h" |
|---|
| 26 | + |
|---|
| 24 | 27 | /** |
|---|
| 25 | 28 | * generic_fillattr - Fill in the basic attributes from the inode struct |
|---|
| 26 | 29 | * @inode: Inode to use as the source |
|---|
| .. | .. |
|---|
| 45 | 48 | stat->ctime = inode->i_ctime; |
|---|
| 46 | 49 | stat->blksize = i_blocksize(inode); |
|---|
| 47 | 50 | stat->blocks = inode->i_blocks; |
|---|
| 48 | | - |
|---|
| 49 | | - if (IS_NOATIME(inode)) |
|---|
| 50 | | - stat->result_mask &= ~STATX_ATIME; |
|---|
| 51 | | - if (IS_AUTOMOUNT(inode)) |
|---|
| 52 | | - stat->attributes |= STATX_ATTR_AUTOMOUNT; |
|---|
| 53 | 51 | } |
|---|
| 54 | | -EXPORT_SYMBOL(generic_fillattr); |
|---|
| 52 | +EXPORT_SYMBOL_NS(generic_fillattr, ANDROID_GKI_VFS_EXPORT_ONLY); |
|---|
| 55 | 53 | |
|---|
| 56 | 54 | /** |
|---|
| 57 | 55 | * vfs_getattr_nosec - getattr without security checks |
|---|
| 58 | 56 | * @path: file to get attributes from |
|---|
| 59 | 57 | * @stat: structure to return attributes in |
|---|
| 60 | 58 | * @request_mask: STATX_xxx flags indicating what the caller wants |
|---|
| 61 | | - * @query_flags: Query mode (KSTAT_QUERY_FLAGS) |
|---|
| 59 | + * @query_flags: Query mode (AT_STATX_SYNC_TYPE) |
|---|
| 62 | 60 | * |
|---|
| 63 | 61 | * Get attributes without calling security_inode_getattr. |
|---|
| 64 | 62 | * |
|---|
| .. | .. |
|---|
| 73 | 71 | |
|---|
| 74 | 72 | memset(stat, 0, sizeof(*stat)); |
|---|
| 75 | 73 | stat->result_mask |= STATX_BASIC_STATS; |
|---|
| 76 | | - request_mask &= STATX_ALL; |
|---|
| 77 | | - query_flags &= KSTAT_QUERY_FLAGS; |
|---|
| 74 | + query_flags &= AT_STATX_SYNC_TYPE; |
|---|
| 75 | + |
|---|
| 76 | + /* allow the fs to override these if it really wants to */ |
|---|
| 77 | + /* SB_NOATIME means filesystem supplies dummy atime value */ |
|---|
| 78 | + if (inode->i_sb->s_flags & SB_NOATIME) |
|---|
| 79 | + stat->result_mask &= ~STATX_ATIME; |
|---|
| 80 | + |
|---|
| 81 | + /* |
|---|
| 82 | + * Note: If you add another clause to set an attribute flag, please |
|---|
| 83 | + * update attributes_mask below. |
|---|
| 84 | + */ |
|---|
| 85 | + if (IS_AUTOMOUNT(inode)) |
|---|
| 86 | + stat->attributes |= STATX_ATTR_AUTOMOUNT; |
|---|
| 87 | + |
|---|
| 88 | + if (IS_DAX(inode)) |
|---|
| 89 | + stat->attributes |= STATX_ATTR_DAX; |
|---|
| 90 | + |
|---|
| 91 | + stat->attributes_mask |= (STATX_ATTR_AUTOMOUNT | |
|---|
| 92 | + STATX_ATTR_DAX); |
|---|
| 93 | + |
|---|
| 78 | 94 | if (inode->i_op->getattr) |
|---|
| 79 | 95 | return inode->i_op->getattr(path, stat, request_mask, |
|---|
| 80 | 96 | query_flags); |
|---|
| .. | .. |
|---|
| 89 | 105 | * @path: The file of interest |
|---|
| 90 | 106 | * @stat: Where to return the statistics |
|---|
| 91 | 107 | * @request_mask: STATX_xxx flags indicating what the caller wants |
|---|
| 92 | | - * @query_flags: Query mode (KSTAT_QUERY_FLAGS) |
|---|
| 108 | + * @query_flags: Query mode (AT_STATX_SYNC_TYPE) |
|---|
| 93 | 109 | * |
|---|
| 94 | 110 | * Ask the filesystem for a file's attributes. The caller must indicate in |
|---|
| 95 | 111 | * request_mask and query_flags to indicate what they want. |
|---|
| .. | .. |
|---|
| 118 | 134 | EXPORT_SYMBOL(vfs_getattr); |
|---|
| 119 | 135 | |
|---|
| 120 | 136 | /** |
|---|
| 121 | | - * vfs_statx_fd - Get the enhanced basic attributes by file descriptor |
|---|
| 137 | + * vfs_fstat - Get the basic attributes by file descriptor |
|---|
| 122 | 138 | * @fd: The file descriptor referring to the file of interest |
|---|
| 123 | 139 | * @stat: The result structure to fill in. |
|---|
| 124 | | - * @request_mask: STATX_xxx flags indicating what the caller wants |
|---|
| 125 | | - * @query_flags: Query mode (KSTAT_QUERY_FLAGS) |
|---|
| 126 | 140 | * |
|---|
| 127 | 141 | * This function is a wrapper around vfs_getattr(). The main difference is |
|---|
| 128 | 142 | * that it uses a file descriptor to determine the file location. |
|---|
| 129 | 143 | * |
|---|
| 130 | 144 | * 0 will be returned on success, and a -ve error code if unsuccessful. |
|---|
| 131 | 145 | */ |
|---|
| 132 | | -int vfs_statx_fd(unsigned int fd, struct kstat *stat, |
|---|
| 133 | | - u32 request_mask, unsigned int query_flags) |
|---|
| 146 | +int vfs_fstat(int fd, struct kstat *stat) |
|---|
| 134 | 147 | { |
|---|
| 135 | 148 | struct fd f; |
|---|
| 136 | | - int error = -EBADF; |
|---|
| 137 | | - |
|---|
| 138 | | - if (query_flags & ~KSTAT_QUERY_FLAGS) |
|---|
| 139 | | - return -EINVAL; |
|---|
| 149 | + int error; |
|---|
| 140 | 150 | |
|---|
| 141 | 151 | f = fdget_raw(fd); |
|---|
| 142 | | - if (f.file) { |
|---|
| 143 | | - error = vfs_getattr(&f.file->f_path, stat, |
|---|
| 144 | | - request_mask, query_flags); |
|---|
| 145 | | - fdput(f); |
|---|
| 146 | | - } |
|---|
| 152 | + if (!f.file) |
|---|
| 153 | + return -EBADF; |
|---|
| 154 | + error = vfs_getattr(&f.file->f_path, stat, STATX_BASIC_STATS, 0); |
|---|
| 155 | + fdput(f); |
|---|
| 147 | 156 | return error; |
|---|
| 148 | 157 | } |
|---|
| 149 | | -EXPORT_SYMBOL(vfs_statx_fd); |
|---|
| 150 | 158 | |
|---|
| 151 | 159 | /** |
|---|
| 152 | 160 | * vfs_statx - Get basic and extra attributes by filename |
|---|
| .. | .. |
|---|
| 163 | 171 | * |
|---|
| 164 | 172 | * 0 will be returned on success, and a -ve error code if unsuccessful. |
|---|
| 165 | 173 | */ |
|---|
| 166 | | -int vfs_statx(int dfd, const char __user *filename, int flags, |
|---|
| 174 | +static int vfs_statx(int dfd, const char __user *filename, int flags, |
|---|
| 167 | 175 | struct kstat *stat, u32 request_mask) |
|---|
| 168 | 176 | { |
|---|
| 169 | 177 | struct path path; |
|---|
| 170 | | - int error = -EINVAL; |
|---|
| 171 | | - unsigned int lookup_flags = LOOKUP_FOLLOW | LOOKUP_AUTOMOUNT; |
|---|
| 178 | + unsigned lookup_flags = 0; |
|---|
| 179 | + int error; |
|---|
| 172 | 180 | |
|---|
| 173 | | - if ((flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | |
|---|
| 174 | | - AT_EMPTY_PATH | KSTAT_QUERY_FLAGS)) != 0) |
|---|
| 181 | + if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH | |
|---|
| 182 | + AT_STATX_SYNC_TYPE)) |
|---|
| 175 | 183 | return -EINVAL; |
|---|
| 176 | 184 | |
|---|
| 177 | | - if (flags & AT_SYMLINK_NOFOLLOW) |
|---|
| 178 | | - lookup_flags &= ~LOOKUP_FOLLOW; |
|---|
| 179 | | - if (flags & AT_NO_AUTOMOUNT) |
|---|
| 180 | | - lookup_flags &= ~LOOKUP_AUTOMOUNT; |
|---|
| 185 | + if (!(flags & AT_SYMLINK_NOFOLLOW)) |
|---|
| 186 | + lookup_flags |= LOOKUP_FOLLOW; |
|---|
| 187 | + if (!(flags & AT_NO_AUTOMOUNT)) |
|---|
| 188 | + lookup_flags |= LOOKUP_AUTOMOUNT; |
|---|
| 181 | 189 | if (flags & AT_EMPTY_PATH) |
|---|
| 182 | 190 | lookup_flags |= LOOKUP_EMPTY; |
|---|
| 183 | 191 | |
|---|
| .. | .. |
|---|
| 187 | 195 | goto out; |
|---|
| 188 | 196 | |
|---|
| 189 | 197 | error = vfs_getattr(&path, stat, request_mask, flags); |
|---|
| 198 | + stat->mnt_id = real_mount(path.mnt)->mnt_id; |
|---|
| 199 | + stat->result_mask |= STATX_MNT_ID; |
|---|
| 200 | + if (path.mnt->mnt_root == path.dentry) |
|---|
| 201 | + stat->attributes |= STATX_ATTR_MOUNT_ROOT; |
|---|
| 202 | + stat->attributes_mask |= STATX_ATTR_MOUNT_ROOT; |
|---|
| 190 | 203 | path_put(&path); |
|---|
| 191 | 204 | if (retry_estale(error, lookup_flags)) { |
|---|
| 192 | 205 | lookup_flags |= LOOKUP_REVAL; |
|---|
| .. | .. |
|---|
| 195 | 208 | out: |
|---|
| 196 | 209 | return error; |
|---|
| 197 | 210 | } |
|---|
| 198 | | -EXPORT_SYMBOL(vfs_statx); |
|---|
| 199 | 211 | |
|---|
| 212 | +int vfs_fstatat(int dfd, const char __user *filename, |
|---|
| 213 | + struct kstat *stat, int flags) |
|---|
| 214 | +{ |
|---|
| 215 | + return vfs_statx(dfd, filename, flags | AT_NO_AUTOMOUNT, |
|---|
| 216 | + stat, STATX_BASIC_STATS); |
|---|
| 217 | +} |
|---|
| 200 | 218 | |
|---|
| 201 | 219 | #ifdef __ARCH_WANT_OLD_STAT |
|---|
| 202 | 220 | |
|---|
| .. | .. |
|---|
| 280 | 298 | |
|---|
| 281 | 299 | #endif /* __ARCH_WANT_OLD_STAT */ |
|---|
| 282 | 300 | |
|---|
| 301 | +#ifdef __ARCH_WANT_NEW_STAT |
|---|
| 302 | + |
|---|
| 283 | 303 | #if BITS_PER_LONG == 32 |
|---|
| 284 | 304 | # define choose_32_64(a,b) a |
|---|
| 285 | 305 | #else |
|---|
| 286 | 306 | # define choose_32_64(a,b) b |
|---|
| 287 | 307 | #endif |
|---|
| 288 | | - |
|---|
| 289 | | -#define valid_dev(x) choose_32_64(old_valid_dev(x),true) |
|---|
| 290 | | -#define encode_dev(x) choose_32_64(old_encode_dev,new_encode_dev)(x) |
|---|
| 291 | 308 | |
|---|
| 292 | 309 | #ifndef INIT_STRUCT_STAT_PADDING |
|---|
| 293 | 310 | # define INIT_STRUCT_STAT_PADDING(st) memset(&st, 0, sizeof(st)) |
|---|
| .. | .. |
|---|
| 297 | 314 | { |
|---|
| 298 | 315 | struct stat tmp; |
|---|
| 299 | 316 | |
|---|
| 300 | | - if (!valid_dev(stat->dev) || !valid_dev(stat->rdev)) |
|---|
| 317 | + if (sizeof(tmp.st_dev) < 4 && !old_valid_dev(stat->dev)) |
|---|
| 318 | + return -EOVERFLOW; |
|---|
| 319 | + if (sizeof(tmp.st_rdev) < 4 && !old_valid_dev(stat->rdev)) |
|---|
| 301 | 320 | return -EOVERFLOW; |
|---|
| 302 | 321 | #if BITS_PER_LONG == 32 |
|---|
| 303 | 322 | if (stat->size > MAX_NON_LFS) |
|---|
| .. | .. |
|---|
| 305 | 324 | #endif |
|---|
| 306 | 325 | |
|---|
| 307 | 326 | INIT_STRUCT_STAT_PADDING(tmp); |
|---|
| 308 | | - tmp.st_dev = encode_dev(stat->dev); |
|---|
| 327 | + tmp.st_dev = new_encode_dev(stat->dev); |
|---|
| 309 | 328 | tmp.st_ino = stat->ino; |
|---|
| 310 | 329 | if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino) |
|---|
| 311 | 330 | return -EOVERFLOW; |
|---|
| .. | .. |
|---|
| 315 | 334 | return -EOVERFLOW; |
|---|
| 316 | 335 | SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid)); |
|---|
| 317 | 336 | SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid)); |
|---|
| 318 | | - tmp.st_rdev = encode_dev(stat->rdev); |
|---|
| 337 | + tmp.st_rdev = new_encode_dev(stat->rdev); |
|---|
| 319 | 338 | tmp.st_size = stat->size; |
|---|
| 320 | 339 | tmp.st_atime = stat->atime.tv_sec; |
|---|
| 321 | 340 | tmp.st_mtime = stat->mtime.tv_sec; |
|---|
| .. | .. |
|---|
| 378 | 397 | |
|---|
| 379 | 398 | return error; |
|---|
| 380 | 399 | } |
|---|
| 400 | +#endif |
|---|
| 381 | 401 | |
|---|
| 382 | 402 | static int do_readlinkat(int dfd, const char __user *pathname, |
|---|
| 383 | 403 | char __user *buf, int bufsiz) |
|---|
| .. | .. |
|---|
| 548 | 568 | tmp.stx_rdev_minor = MINOR(stat->rdev); |
|---|
| 549 | 569 | tmp.stx_dev_major = MAJOR(stat->dev); |
|---|
| 550 | 570 | tmp.stx_dev_minor = MINOR(stat->dev); |
|---|
| 571 | + tmp.stx_mnt_id = stat->mnt_id; |
|---|
| 551 | 572 | |
|---|
| 552 | 573 | return copy_to_user(buffer, &tmp, sizeof(tmp)) ? -EFAULT : 0; |
|---|
| 574 | +} |
|---|
| 575 | + |
|---|
| 576 | +int do_statx(int dfd, const char __user *filename, unsigned flags, |
|---|
| 577 | + unsigned int mask, struct statx __user *buffer) |
|---|
| 578 | +{ |
|---|
| 579 | + struct kstat stat; |
|---|
| 580 | + int error; |
|---|
| 581 | + |
|---|
| 582 | + if (mask & STATX__RESERVED) |
|---|
| 583 | + return -EINVAL; |
|---|
| 584 | + if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE) |
|---|
| 585 | + return -EINVAL; |
|---|
| 586 | + |
|---|
| 587 | + error = vfs_statx(dfd, filename, flags, &stat, mask); |
|---|
| 588 | + if (error) |
|---|
| 589 | + return error; |
|---|
| 590 | + |
|---|
| 591 | + return cp_statx(&stat, buffer); |
|---|
| 553 | 592 | } |
|---|
| 554 | 593 | |
|---|
| 555 | 594 | /** |
|---|
| .. | .. |
|---|
| 568 | 607 | unsigned int, mask, |
|---|
| 569 | 608 | struct statx __user *, buffer) |
|---|
| 570 | 609 | { |
|---|
| 571 | | - struct kstat stat; |
|---|
| 572 | | - int error; |
|---|
| 573 | | - |
|---|
| 574 | | - if (mask & STATX__RESERVED) |
|---|
| 575 | | - return -EINVAL; |
|---|
| 576 | | - if ((flags & AT_STATX_SYNC_TYPE) == AT_STATX_SYNC_TYPE) |
|---|
| 577 | | - return -EINVAL; |
|---|
| 578 | | - |
|---|
| 579 | | - error = vfs_statx(dfd, filename, flags, &stat, mask); |
|---|
| 580 | | - if (error) |
|---|
| 581 | | - return error; |
|---|
| 582 | | - |
|---|
| 583 | | - return cp_statx(&stat, buffer); |
|---|
| 610 | + return do_statx(dfd, filename, flags, mask, buffer); |
|---|
| 584 | 611 | } |
|---|
| 585 | 612 | |
|---|
| 586 | 613 | #ifdef CONFIG_COMPAT |
|---|
| .. | .. |
|---|
| 588 | 615 | { |
|---|
| 589 | 616 | struct compat_stat tmp; |
|---|
| 590 | 617 | |
|---|
| 591 | | - if (!old_valid_dev(stat->dev) || !old_valid_dev(stat->rdev)) |
|---|
| 618 | + if (sizeof(tmp.st_dev) < 4 && !old_valid_dev(stat->dev)) |
|---|
| 619 | + return -EOVERFLOW; |
|---|
| 620 | + if (sizeof(tmp.st_rdev) < 4 && !old_valid_dev(stat->rdev)) |
|---|
| 592 | 621 | return -EOVERFLOW; |
|---|
| 593 | 622 | |
|---|
| 594 | 623 | memset(&tmp, 0, sizeof(tmp)); |
|---|
| 595 | | - tmp.st_dev = old_encode_dev(stat->dev); |
|---|
| 624 | + tmp.st_dev = new_encode_dev(stat->dev); |
|---|
| 596 | 625 | tmp.st_ino = stat->ino; |
|---|
| 597 | 626 | if (sizeof(tmp.st_ino) < sizeof(stat->ino) && tmp.st_ino != stat->ino) |
|---|
| 598 | 627 | return -EOVERFLOW; |
|---|
| .. | .. |
|---|
| 602 | 631 | return -EOVERFLOW; |
|---|
| 603 | 632 | SET_UID(tmp.st_uid, from_kuid_munged(current_user_ns(), stat->uid)); |
|---|
| 604 | 633 | SET_GID(tmp.st_gid, from_kgid_munged(current_user_ns(), stat->gid)); |
|---|
| 605 | | - tmp.st_rdev = old_encode_dev(stat->rdev); |
|---|
| 634 | + tmp.st_rdev = new_encode_dev(stat->rdev); |
|---|
| 606 | 635 | if ((u64) stat->size > MAX_NON_LFS) |
|---|
| 607 | 636 | return -EOVERFLOW; |
|---|
| 608 | 637 | tmp.st_size = stat->size; |
|---|