Commit 44e843eb authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'for-rc-adfs' of git://git.armlinux.org.uk/~rmk/linux-arm

Pull ADFS cleanups/fixes from Russell King:
 "As a result of some of Al Viro's great work, here are a few cleanups
  with fixes for adfs:

   - factor out filename comparison, so we can be sure that
     adfs_compare() (used for namei compare) and adfs_match() (used for
     lookup) have the same behaviour.

   - factor out filename lowering (which is not the same as tolower()
     which will lower top-bit-set characters) to ensure that we have the
     same behaviour when comparing filenames as when we hash them.

   - factor out the object fixups, so we are applying all fixups to
     directory objects in the same way, independent of the disk format.

   - factor out the object name fixup (into the previously factored out
     function) to ensure that filenames are appropriately translated -
     for example, adfs allows '/' in filenames, which being the Unix
     path separator, need to be translated to a different character,
     which is normally '.' (DOS 8.3 filenames represent the . as a / on
     adfs, so this is the expected reverse translation.)

   - remove filename truncation; Al asked about this and apparently the
     decision is to remove it. In any case, adfs's truncation was buggy,
     so this rids us of that bug by removing the truncation feature.

   - we now have only one location which adds the "filetype" suffix to
     the filename, so there's no point that code being out of line.

   - since we translate '/' into '.', an adfs filename of "/" or "//"
     would end up being translated to "." and ".." which have special
     meanings. In this case, change the first character to "^" to avoid
     these special directory names being abused"

* tag 'for-rc-adfs' of git://git.armlinux.org.uk/~rmk/linux-arm:
  fs/adfs: fix filename fixup handling for "/" and "//" names
  fs/adfs: move append_filetype_suffix() into adfs_object_fixup()
  fs/adfs: remove truncated filename hashing
  fs/adfs: factor out filename fixup
  fs/adfs: factor out object fixups
  fs/adfs: factor out filename case lowering
  fs/adfs: factor out filename comparison
parents 156c0591 fc722a04
Loading
Loading
Loading
Loading
+1 −13
Original line number Diff line number Diff line
@@ -113,19 +113,6 @@ struct object_info {
	__u16		filetype;
};

/* RISC OS 12-bit filetype converts to ,xyz hex filename suffix */
static inline int append_filetype_suffix(char *buf, __u16 filetype)
{
	if (filetype == 0xffff)	/* no explicit 12-bit file type was set */
		return 0;

	*buf++ = ',';
	*buf++ = hex_asc_lo(filetype >> 8);
	*buf++ = hex_asc_lo(filetype >> 4);
	*buf++ = hex_asc_lo(filetype >> 0);
	return 4;
}

struct adfs_dir_ops {
	int	(*read)(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir);
	int	(*setpos)(struct adfs_dir *dir, unsigned int fpos);
@@ -172,6 +159,7 @@ extern const struct dentry_operations adfs_dentry_operations;
extern const struct adfs_dir_ops adfs_f_dir_ops;
extern const struct adfs_dir_ops adfs_fplus_dir_ops;

void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj);
extern int adfs_dir_update(struct super_block *sb, struct object_info *obj,
			   int wait);

+75 −62
Original line number Diff line number Diff line
@@ -16,6 +16,50 @@
 */
static DEFINE_RWLOCK(adfs_dir_lock);

void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
{
	unsigned int dots, i;

	/*
	 * RISC OS allows the use of '/' in directory entry names, so we need
	 * to fix these up.  '/' is typically used for FAT compatibility to
	 * represent '.', so do the same conversion here.  In any case, '.'
	 * will never be in a RISC OS name since it is used as the pathname
	 * separator.  Handle the case where we may generate a '.' or '..'
	 * name, replacing the first character with '^' (the RISC OS "parent
	 * directory" character.)
	 */
	for (i = dots = 0; i < obj->name_len; i++)
		if (obj->name[i] == '/') {
			obj->name[i] = '.';
			dots++;
		}

	if (obj->name_len <= 2 && dots == obj->name_len)
		obj->name[0] = '^';

	obj->filetype = -1;

	/*
	 * object is a file and is filetyped and timestamped?
	 * RISC OS 12-bit filetype is stored in load_address[19:8]
	 */
	if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) &&
	    (0xfff00000 == (0xfff00000 & obj->loadaddr))) {
		obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8);

		/* optionally append the ,xyz hex filetype suffix */
		if (ADFS_SB(dir->sb)->s_ftsuffix) {
			__u16 filetype = obj->filetype;

			obj->name[obj->name_len++] = ',';
			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
		}
	}
}

static int
adfs_readdir(struct file *file, struct dir_context *ctx)
{
@@ -100,37 +144,36 @@ out:
	return ret;
}

static int
adfs_match(const struct qstr *name, struct object_info *obj)
static unsigned char adfs_tolower(unsigned char c)
{
	int i;

	if (name->len != obj->name_len)
		return 0;
	if (c >= 'A' && c <= 'Z')
		c += 'a' - 'A';
	return c;
}

	for (i = 0; i < name->len; i++) {
		char c1, c2;
static int __adfs_compare(const unsigned char *qstr, u32 qlen,
			  const char *str, u32 len)
{
	u32 i;

		c1 = name->name[i];
		c2 = obj->name[i];
	if (qlen != len)
		return 1;

		if (c1 >= 'A' && c1 <= 'Z')
			c1 += 'a' - 'A';
		if (c2 >= 'A' && c2 <= 'Z')
			c2 += 'a' - 'A';
	for (i = 0; i < qlen; i++)
		if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
			return 1;

		if (c1 != c2)
	return 0;
}
	return 1;
}

static int
adfs_dir_lookup_byname(struct inode *inode, const struct qstr *name, struct object_info *obj)
static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
				  struct object_info *obj)
{
	struct super_block *sb = inode->i_sb;
	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
	const unsigned char *name;
	struct adfs_dir dir;
	u32 name_len;
	int ret;

	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
@@ -153,8 +196,10 @@ adfs_dir_lookup_byname(struct inode *inode, const struct qstr *name, struct obje
		goto unlock_out;

	ret = -ENOENT;
	name = qstr->name;
	name_len = qstr->len;
	while (ops->getnext(&dir, obj) == 0) {
		if (adfs_match(name, obj)) {
		if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
			ret = 0;
			break;
		}
@@ -179,30 +224,18 @@ const struct file_operations adfs_dir_operations = {
static int
adfs_hash(const struct dentry *parent, struct qstr *qstr)
{
	const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen;
	const unsigned char *name;
	unsigned long hash;
	int i;
	u32 len;

	if (qstr->len < name_len)
		return 0;
	if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
		return -ENAMETOOLONG;

	/*
	 * Truncate the name in place, avoids
	 * having to define a compare function.
	 */
	qstr->len = i = name_len;
	len = qstr->len;
	name = qstr->name;
	hash = init_name_hash(parent);
	while (i--) {
		char c;

		c = *name++;
		if (c >= 'A' && c <= 'Z')
			c += 'a' - 'A';

		hash = partial_name_hash(c, hash);
	}
	while (len--)
		hash = partial_name_hash(adfs_tolower(*name++), hash);
	qstr->hash = end_name_hash(hash);

	return 0;
@@ -212,30 +245,10 @@ adfs_hash(const struct dentry *parent, struct qstr *qstr)
 * Compare two names, taking note of the name length
 * requirements of the underlying filesystem.
 */
static int
adfs_compare(const struct dentry *dentry,
		unsigned int len, const char *str, const struct qstr *name)
static int adfs_compare(const struct dentry *dentry, unsigned int len,
			const char *str, const struct qstr *qstr)
{
	int i;

	if (len != name->len)
		return 1;

	for (i = 0; i < name->len; i++) {
		char a, b;

		a = str[i];
		b = name->name[i];

		if (a >= 'A' && a <= 'Z')
			a += 'a' - 'A';
		if (b >= 'A' && b <= 'Z')
			b += 'a' - 'A';

		if (a != b)
			return 1;
	}
	return 0;
	return __adfs_compare(qstr->name, qstr->len, str, len);
}

const struct dentry_operations adfs_dentry_operations = {
+11 −32
Original line number Diff line number Diff line
@@ -47,21 +47,6 @@ static inline void adfs_writeval(unsigned char *p, int len, unsigned int val)
	}
}

static inline int adfs_readname(char *buf, char *ptr, int maxlen)
{
	char *old_buf = buf;

	while ((unsigned char)*ptr >= ' ' && maxlen--) {
		if (*ptr == '/')
			*buf++ = '.';
		else
			*buf++ = *ptr;
		ptr++;
	}

	return buf - old_buf;
}

#define ror13(v) ((v >> 13) | (v << 19))

#define dir_u8(idx)				\
@@ -216,29 +201,23 @@ static inline void
adfs_dir2obj(struct adfs_dir *dir, struct object_info *obj,
	struct adfs_direntry *de)
{
	obj->name_len =	adfs_readname(obj->name, de->dirobname, ADFS_F_NAME_LEN);
	unsigned int name_len;

	for (name_len = 0; name_len < ADFS_F_NAME_LEN; name_len++) {
		if (de->dirobname[name_len] < ' ')
			break;

		obj->name[name_len] = de->dirobname[name_len];
	}

	obj->name_len =	name_len;
	obj->file_id  = adfs_readval(de->dirinddiscadd, 3);
	obj->loadaddr = adfs_readval(de->dirload, 4);
	obj->execaddr = adfs_readval(de->direxec, 4);
	obj->size     = adfs_readval(de->dirlen,  4);
	obj->attr     = de->newdiratts;
	obj->filetype = -1;

	/*
	 * object is a file and is filetyped and timestamped?
	 * RISC OS 12-bit filetype is stored in load_address[19:8]
	 */
	if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) &&
		(0xfff00000 == (0xfff00000 & obj->loadaddr))) {
		obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8);

		/* optionally append the ,xyz hex filetype suffix */
		if (ADFS_SB(dir->sb)->s_ftsuffix)
			obj->name_len +=
				append_filetype_suffix(
					&obj->name[obj->name_len],
					obj->filetype);
	}
	adfs_object_fixup(dir, obj);
}

/*
+2 −22
Original line number Diff line number Diff line
@@ -169,7 +169,7 @@ adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
		(struct adfs_bigdirheader *) dir->bh_fplus[0]->b_data;
	struct adfs_bigdirentry bde;
	unsigned int offset;
	int i, ret = -ENOENT;
	int ret = -ENOENT;

	if (dir->pos >= le32_to_cpu(h->bigdirentries))
		goto out;
@@ -193,27 +193,7 @@ adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
	offset += le32_to_cpu(bde.bigdirobnameptr);

	dir_memcpy(dir, offset, obj->name, obj->name_len);
	for (i = 0; i < obj->name_len; i++)
		if (obj->name[i] == '/')
			obj->name[i] = '.';

	obj->filetype = -1;

	/*
	 * object is a file and is filetyped and timestamped?
	 * RISC OS 12-bit filetype is stored in load_address[19:8]
	 */
	if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) &&
		(0xfff00000 == (0xfff00000 & obj->loadaddr))) {
		obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8);

		/* optionally append the ,xyz hex filetype suffix */
		if (ADFS_SB(dir->sb)->s_ftsuffix)
			obj->name_len +=
				append_filetype_suffix(
					&obj->name[obj->name_len],
					obj->filetype);
	}
	adfs_object_fixup(dir, obj);

	dir->pos += 1;
	ret = 0;