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. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIVAwUAXPD4oPTnkBvkraxkAQK35Q//bql8SDnF9sqy8YlVUmAgoIMdyQtiFuD7 5UgRBPDl4bIm2Y+mtuYjU/u3nq6tZit0HukybUvD5yA64Opy1Ahkf8OB/f0f6TTU xjx55/D9QRj4loGxXHM6PuEO9GX+pIsYPFQoufPYq7hksQB2y1ETkjENk4W4PY2m gvbtQqQmz/B+G7PZvrMsZQV/BwYF3vhP8S/qLbgl3PAbciVofruXtPJNRxgOL8ot hbOIfT5x30YZpILzXqDZJq4mviWPru+FVJ1uIW1Nd5s8T/9seICxXMjFaMQJUSMn oIHCzC1WlP4uRbjwmJ+lyLlEPyYrgYN3+H1FcIO0MTYfBXwYZrVdFLW4TtWBracc 8bRa+p9jeRe9jdlKpGaX12a4W7xQ3SmB2i8UFE+/epnBqPvuDPOM0h19XK+FclTH fAg7Ej1uBC1ROkTlW4OXBsLakXvIIka859DQYQduVKDw8kTSH4QyTdAE7qvOGz/d Y0XMeIUz+U1izVJ8ShHCVyttXnkBkC6Xwoc6RY3IET++Fu0hXCMdQMSf6Ta8zJjA EUAdb3GLYdJTqX6Oy9NTUd42GPnZwR5KMUOJd6v9ETU7gLDRDyu9ILpgrF+vFaKj Xnf7B+D4l1jdB5cU/MYzfzF7Ky80vDHjVr62PSvtb5X9F0pHOINgZJ5an8n80bDc dxQq92h9hCI= =mQU/ -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
44e843eb5c
|
@ -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);
|
||||
|
||||
|
|
145
fs/adfs/dir.c
145
fs/adfs/dir.c
|
@ -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 @@ adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
|
|||
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;
|
||||
|
||||
for (i = 0; i < name->len; i++) {
|
||||
char c1, c2;
|
||||
|
||||
c1 = name->name[i];
|
||||
c2 = obj->name[i];
|
||||
|
||||
if (c1 >= 'A' && c1 <= 'Z')
|
||||
c1 += 'a' - 'A';
|
||||
if (c2 >= 'A' && c2 <= 'Z')
|
||||
c2 += 'a' - 'A';
|
||||
|
||||
if (c1 != c2)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
c += 'a' - 'A';
|
||||
return c;
|
||||
}
|
||||
|
||||
static int
|
||||
adfs_dir_lookup_byname(struct inode *inode, const struct qstr *name, struct object_info *obj)
|
||||
static int __adfs_compare(const unsigned char *qstr, u32 qlen,
|
||||
const char *str, u32 len)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
if (qlen != len)
|
||||
return 1;
|
||||
|
||||
for (i = 0; i < qlen; i++)
|
||||
if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 = {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue
Block a user