mirror of https://github.com/OpenIPC/firmware.git
404 lines
10 KiB
Diff
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;
|