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;
 |