firmware/br-ext-chip-goke/board/gk7205v200/kernel/patches/00_fs-fat-dir.c.patch

404 lines
10 KiB
Diff

--- linux-4.9.37/fs/fat/dir.c 2017-07-12 16:42:41.000000000 +0300
+++ linux-4.9.y/fs/fat/dir.c 2021-06-07 13:01:34.000000000 +0300
@@ -782,6 +782,388 @@
ret = buf.result;
return ret;
}
+#ifdef CONFIG_GOKE_MC
+/*
+ * This is the "fatfilldirall_t" function type,
+ * used by fat_ioctl_filldirall to let
+ * the kernel specify what kind of dirent layout it wants to have.
+ * This allows the kernel to read directories into kernel space or
+ * to have different dirent layouts depending on the binary type.
+ */
+typedef int (*fatfilldirall_t)(void *__buf, const char *name,
+ int name_len, loff_t offset, u64 ino,
+ unsigned int d_type, struct msdos_dir_entry *de,
+ char *d_createtime);
+struct fatdirall_context {
+ const fatfilldirall_t actor;
+ loff_t pos;
+};
+
+struct fat_ioctl_filldirall_callback {
+ struct fatdirall_context ctx;
+ struct fat_direntall __user *current_dir;
+ struct fat_direntall __user *previous;
+ int count;
+ int usecount;
+ int error;
+ int result;
+ const char *longname;
+ int long_len;
+ const char *shortname;
+ int short_len;
+};
+
+static inline bool fat_dir_emit(struct fatdirall_context *ctx,
+ const char *name, int namelen,
+ u64 ino, unsigned type,
+ struct msdos_dir_entry *de,
+ char *d_createtime)
+{
+ return ctx->actor(ctx, name, namelen, ctx->pos, ino,
+ type, de, d_createtime) == 0;
+}
+static inline bool fat_dir_emit_dot(struct file *file,
+ struct fatdirall_context *ctx,
+ struct msdos_dir_entry *de,
+ char *d_createtime)
+{
+ return ctx->actor(ctx, ".", 1, ctx->pos,
+ file->f_path.dentry->d_inode->i_ino,
+ DT_DIR, de, d_createtime) == 0;
+}
+static inline bool fat_dir_emit_dotdot(struct file *file,
+ struct fatdirall_context *ctx,
+ struct msdos_dir_entry *de,
+ char *d_createtime)
+{
+ return ctx->actor(ctx, "..", 2, ctx->pos,
+ parent_ino(file->f_path.dentry),
+ DT_DIR, de, d_createtime) == 0;
+}
+
+static inline bool fat_dir_emit_dots(struct file *file,
+ struct fatdirall_context *ctx,
+ struct msdos_dir_entry *de,
+ char *d_createtime)
+{
+ if (ctx->pos == 0) {
+ if (!fat_dir_emit_dot(file, ctx, de, d_createtime))
+ return false;
+ ctx->pos = 1;
+ }
+ if (ctx->pos == 1) {
+ if (!fat_dir_emit_dotdot(file, ctx, de, d_createtime))
+ return false;
+ ctx->pos = 2;
+ }
+ return true;
+}
+
+
+static int __fat_readdirall(struct inode *inode, struct file *file,
+ struct fatdirall_context *ctx, int short_only,
+ struct fat_ioctl_filldirall_callback *both)
+{
+ struct super_block *sb = inode->i_sb;
+ struct msdos_sb_info *sbi = MSDOS_SB(sb);
+ struct buffer_head *bh;
+ struct msdos_dir_entry *de;
+ unsigned char nr_slots;
+ wchar_t *unicode = NULL;
+ unsigned char bufname[FAT_MAX_SHORT_SIZE];
+ int isvfat = sbi->options.isvfat;
+ const char *fill_name = NULL;
+ int fake_offset = 0;
+ loff_t cpos;
+ int short_len = 0, fill_len = 0;
+ int ret = 0;
+ char d_createtime[8];
+
+ mutex_lock(&sbi->s_lock);
+
+ cpos = ctx->pos;
+ /* Fake . and .. for the root directory. */
+ if (inode->i_ino == MSDOS_ROOT_INO) {
+ if (!fat_dir_emit_dots(file, ctx, NULL, NULL))
+ goto out;
+ if (ctx->pos == 2) {
+ fake_offset = 1;
+ cpos = 0;
+ }
+ }
+ if (cpos & (sizeof(struct msdos_dir_entry) - 1)) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ bh = NULL;
+get_new:
+ if (fat_get_entry(inode, &cpos, &bh, &de) == -1)
+ goto end_of_dir;
+parse_record:
+ nr_slots = 0;
+ /*
+ * Check for long filename entry, but if short_only, we don't
+ * need to parse long filename.
+ */
+ if (isvfat && !short_only) {
+ if (de->name[0] == DELETED_FLAG)
+ goto record_end;
+ if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME))
+ goto record_end;
+ if (de->attr != ATTR_EXT && IS_FREE(de->name))
+ goto record_end;
+ } else {
+ if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name))
+ goto record_end;
+ }
+
+ if (isvfat && de->attr == ATTR_EXT) {
+ int status = fat_parse_long(inode, &cpos, &bh, &de,
+ &unicode, &nr_slots);
+ if (status < 0) {
+ ctx->pos = cpos;
+ ret = status;
+ goto out;
+ } else if (status == PARSE_INVALID)
+ goto record_end;
+ else if (status == PARSE_NOT_LONGNAME)
+ goto parse_record;
+ else if (status == PARSE_EOF)
+ goto end_of_dir;
+
+ if (nr_slots) {
+ void *longname = unicode + FAT_MAX_UNI_CHARS;
+ int size = PATH_MAX - FAT_MAX_UNI_SIZE;
+ int len = fat_uni_to_x8(sb, unicode, longname, size);
+
+ fill_name = longname;
+ fill_len = len;
+
+ short_len = fat_parse_short(sb, de, bufname,
+ sbi->options.dotsOK);
+ if (short_len == 0)
+ goto record_end;
+
+ /* hack for fat_ioctl_filldir() */
+ both->longname = fill_name;
+ both->long_len = fill_len;
+ both->shortname = bufname;
+ both->short_len = short_len;
+ fill_name = NULL;
+ fill_len = 0;
+ goto start_filldir;
+ }
+ }
+
+ short_len = fat_parse_short(sb, de, bufname, sbi->options.dotsOK);
+ if (short_len == 0)
+ goto record_end;
+
+ fill_name = bufname;
+ fill_len = short_len;
+
+start_filldir:
+ if (!fake_offset)
+ ctx->pos = cpos - (nr_slots + 1)
+ * sizeof(struct msdos_dir_entry);
+
+ memset(d_createtime, 0, 8);
+ fat_time_fat2str(sbi, d_createtime, de->ctime,
+ de->cdate, de->ctime_cs);
+
+ if (!memcmp(de->name, MSDOS_DOT, MSDOS_NAME)) {
+ if (!fat_dir_emit_dot(file, ctx, de, d_createtime))
+ goto fill_failed;
+ } else if (!memcmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) {
+ if (!fat_dir_emit_dotdot(file, ctx, de, d_createtime))
+ goto fill_failed;
+ } else {
+ unsigned long inum;
+ loff_t i_pos = fat_make_i_pos(sb, bh, de);
+ struct inode *tmp = fat_iget(sb, i_pos);
+
+ if (tmp) {
+ inum = tmp->i_ino;
+ iput(tmp);
+ } else
+ inum = iunique(sb, MSDOS_ROOT_INO);
+ if (!fat_dir_emit(ctx, fill_name, fill_len, inum,
+ (de->attr & ATTR_DIR) ? DT_DIR : DT_REG,
+ de, d_createtime))
+ goto fill_failed;
+ }
+
+record_end:
+ fake_offset = 0;
+ ctx->pos = cpos;
+ goto get_new;
+end_of_dir:
+ ctx->pos = cpos;
+fill_failed:
+ brelse(bh);
+ if (unicode)
+ __putname(unicode);
+out:
+ mutex_unlock(&sbi->s_lock);
+ return ret;
+}
+
+static int fat_ioctl_filldirall(void *__buf, const char *name,
+ int name_len, loff_t offset,
+ u64 ino, unsigned int d_type,
+ struct msdos_dir_entry *de,
+ char *d_createtime)
+{
+ struct fat_direntall __user *dirent;
+ struct fat_ioctl_filldirall_callback *buf;
+ unsigned long d_ino;
+ int reclen = 0;
+ const char *longname = NULL;
+ int long_len = 0;
+ const char *shortname = NULL;
+ int short_len = 0;
+
+ buf = (struct fat_ioctl_filldirall_callback *) __buf;
+
+ if (name != NULL) {
+ reclen = ALIGN(offsetof(struct fat_direntall, d_name)
+ + name_len + 2, sizeof(long));
+ } else {
+ longname = buf->longname;
+ long_len = buf->long_len;
+ shortname = buf->shortname;
+ short_len = buf->short_len;
+ reclen = ALIGN(offsetof(struct fat_direntall, d_name)
+ + long_len + 2, sizeof(long));
+ }
+
+ buf->error = -EINVAL; /* only used if we fail.. */
+
+ if (reclen >= buf->count)
+ return -EINVAL;
+
+ d_ino = ino;
+
+ if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
+ buf->error = -EOVERFLOW;
+ return -EOVERFLOW;
+ }
+
+ dirent = buf->previous;
+
+ if (dirent) {
+ if (__put_user(offset, &dirent->d_off))
+ goto efault;
+ }
+
+ dirent = buf->current_dir;
+
+ if (__put_user(d_ino, &dirent->d_ino))
+ goto efault;
+
+ if (__put_user(reclen, &dirent->d_reclen))
+ goto efault;
+
+ if (name != NULL) {
+ if (copy_to_user(dirent->d_name, name, name_len))
+ goto efault;
+ if (__put_user(0, dirent->d_name + name_len))
+ goto efault;
+ } else {
+ if (copy_to_user(dirent->d_name, longname, long_len))
+ goto efault;
+ if (__put_user(0, dirent->d_name + long_len))
+ goto efault;
+ }
+
+ if (__put_user(d_type, &dirent->d_type))
+ goto efault;
+
+ if (de != NULL) {
+ u64 u_size = 0;
+ if (copy_to_user(&dirent->d_size, &u_size, sizeof(u64)))
+ goto efault;
+ if (copy_to_user(&dirent->d_size, &de->size, sizeof(u32)))
+ goto efault;
+ }
+
+ if (d_createtime != NULL) {
+ if (copy_to_user(dirent->d_createtime, d_createtime, 8))
+ goto efault;
+ }
+ buf->previous = dirent;
+ dirent = (void __user *)dirent + reclen;
+ buf->current_dir = dirent;
+ buf->count -= reclen;
+ buf->usecount += reclen;
+ return 0;
+efault:
+ buf->error = -EFAULT;
+ return -EFAULT;
+}
+
+
+static int fat_ioctl_readdirall(struct inode *inode, struct file *file,
+ void __user *dirent,
+ int short_only, int both)
+{
+ struct fat_ioctl_filldirall_callback buf = {
+ .ctx.actor = fat_ioctl_filldirall,
+ };
+
+ struct fat_direntall_buf __user *userbuf = dirent;
+ int ret;
+
+ buf.current_dir = &(userbuf->direntall);
+ buf.previous = NULL;
+ buf.error = 0;
+ buf.result = 0;
+ buf.usecount = 0;
+
+ if (get_user(buf.count, &(userbuf->d_count)))
+ return -EFAULT;
+
+ up_read(&inode->i_rwsem);
+ buf.ctx.pos = file->f_pos;
+ ret = -ENOENT;
+ if (!IS_DEADDIR(inode)) {
+ ret = __fat_readdirall(inode, file, &buf.ctx,
+ short_only, both ? &buf : NULL);
+ file->f_pos = buf.ctx.pos;
+ }
+ down_read(&inode->i_rwsem);
+
+ if (__put_user(buf.usecount, &(userbuf->d_usecount)))
+ return -EFAULT;
+ if (ret >= 0)
+ ret = buf.result;
+ return ret;
+}
+
+static int fat_dir_ioctl_readdirall(struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ struct fat_direntall_buf __user *direntallbuf;
+ int short_only, both;
+
+ direntallbuf = (struct fat_direntall_buf __user *)arg;
+
+ if (!access_ok(VERIFY_WRITE, direntallbuf,
+ sizeof(struct fat_direntall_buf)))
+ return -EFAULT;
+ if (put_user(0, &(direntallbuf->direntall.d_reclen)))
+ return -EFAULT;
+ if (put_user(0, &(direntallbuf->d_usecount)))
+ return -EFAULT;
+ short_only = 0;
+ both = 1;
+ return fat_ioctl_readdirall(inode, filp, direntallbuf,
+ short_only, both);
+}
+#endif
+
static long fat_dir_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
@@ -789,7 +1171,10 @@
struct inode *inode = file_inode(filp);
struct __fat_dirent __user *d1 = (struct __fat_dirent __user *)arg;
int short_only, both;
-
+#ifdef CONFIG_GOKE_MC
+ if (VFAT_IOCTL_READDIR_ALL == cmd)
+ return fat_dir_ioctl_readdirall(filp, cmd, arg);
+#endif
switch (cmd) {
case VFAT_IOCTL_READDIR_SHORT:
short_only = 1;