forked from Minki/linux
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;
|
__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 {
|
struct adfs_dir_ops {
|
||||||
int (*read)(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir);
|
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);
|
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_f_dir_ops;
|
||||||
extern const struct adfs_dir_ops adfs_fplus_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,
|
extern int adfs_dir_update(struct super_block *sb, struct object_info *obj,
|
||||||
int wait);
|
int wait);
|
||||||
|
|
||||||
|
145
fs/adfs/dir.c
145
fs/adfs/dir.c
@ -16,6 +16,50 @@
|
|||||||
*/
|
*/
|
||||||
static DEFINE_RWLOCK(adfs_dir_lock);
|
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
|
static int
|
||||||
adfs_readdir(struct file *file, struct dir_context *ctx)
|
adfs_readdir(struct file *file, struct dir_context *ctx)
|
||||||
{
|
{
|
||||||
@ -100,37 +144,36 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static unsigned char adfs_tolower(unsigned char c)
|
||||||
adfs_match(const struct qstr *name, struct object_info *obj)
|
|
||||||
{
|
{
|
||||||
int i;
|
if (c >= 'A' && c <= 'Z')
|
||||||
|
c += 'a' - 'A';
|
||||||
if (name->len != obj->name_len)
|
return c;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int __adfs_compare(const unsigned char *qstr, u32 qlen,
|
||||||
adfs_dir_lookup_byname(struct inode *inode, const struct qstr *name, struct object_info *obj)
|
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;
|
struct super_block *sb = inode->i_sb;
|
||||||
const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
|
const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
|
||||||
|
const unsigned char *name;
|
||||||
struct adfs_dir dir;
|
struct adfs_dir dir;
|
||||||
|
u32 name_len;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
|
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;
|
goto unlock_out;
|
||||||
|
|
||||||
ret = -ENOENT;
|
ret = -ENOENT;
|
||||||
|
name = qstr->name;
|
||||||
|
name_len = qstr->len;
|
||||||
while (ops->getnext(&dir, obj) == 0) {
|
while (ops->getnext(&dir, obj) == 0) {
|
||||||
if (adfs_match(name, obj)) {
|
if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -179,30 +224,18 @@ const struct file_operations adfs_dir_operations = {
|
|||||||
static int
|
static int
|
||||||
adfs_hash(const struct dentry *parent, struct qstr *qstr)
|
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;
|
const unsigned char *name;
|
||||||
unsigned long hash;
|
unsigned long hash;
|
||||||
int i;
|
u32 len;
|
||||||
|
|
||||||
if (qstr->len < name_len)
|
if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
|
||||||
return 0;
|
return -ENAMETOOLONG;
|
||||||
|
|
||||||
/*
|
len = qstr->len;
|
||||||
* Truncate the name in place, avoids
|
|
||||||
* having to define a compare function.
|
|
||||||
*/
|
|
||||||
qstr->len = i = name_len;
|
|
||||||
name = qstr->name;
|
name = qstr->name;
|
||||||
hash = init_name_hash(parent);
|
hash = init_name_hash(parent);
|
||||||
while (i--) {
|
while (len--)
|
||||||
char c;
|
hash = partial_name_hash(adfs_tolower(*name++), hash);
|
||||||
|
|
||||||
c = *name++;
|
|
||||||
if (c >= 'A' && c <= 'Z')
|
|
||||||
c += 'a' - 'A';
|
|
||||||
|
|
||||||
hash = partial_name_hash(c, hash);
|
|
||||||
}
|
|
||||||
qstr->hash = end_name_hash(hash);
|
qstr->hash = end_name_hash(hash);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -212,30 +245,10 @@ adfs_hash(const struct dentry *parent, struct qstr *qstr)
|
|||||||
* Compare two names, taking note of the name length
|
* Compare two names, taking note of the name length
|
||||||
* requirements of the underlying filesystem.
|
* requirements of the underlying filesystem.
|
||||||
*/
|
*/
|
||||||
static int
|
static int adfs_compare(const struct dentry *dentry, unsigned int len,
|
||||||
adfs_compare(const struct dentry *dentry,
|
const char *str, const struct qstr *qstr)
|
||||||
unsigned int len, const char *str, const struct qstr *name)
|
|
||||||
{
|
{
|
||||||
int i;
|
return __adfs_compare(qstr->name, qstr->len, str, len);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct dentry_operations adfs_dentry_operations = {
|
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 ror13(v) ((v >> 13) | (v << 19))
|
||||||
|
|
||||||
#define dir_u8(idx) \
|
#define dir_u8(idx) \
|
||||||
@ -216,29 +201,23 @@ static inline void
|
|||||||
adfs_dir2obj(struct adfs_dir *dir, struct object_info *obj,
|
adfs_dir2obj(struct adfs_dir *dir, struct object_info *obj,
|
||||||
struct adfs_direntry *de)
|
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->file_id = adfs_readval(de->dirinddiscadd, 3);
|
||||||
obj->loadaddr = adfs_readval(de->dirload, 4);
|
obj->loadaddr = adfs_readval(de->dirload, 4);
|
||||||
obj->execaddr = adfs_readval(de->direxec, 4);
|
obj->execaddr = adfs_readval(de->direxec, 4);
|
||||||
obj->size = adfs_readval(de->dirlen, 4);
|
obj->size = adfs_readval(de->dirlen, 4);
|
||||||
obj->attr = de->newdiratts;
|
obj->attr = de->newdiratts;
|
||||||
obj->filetype = -1;
|
|
||||||
|
|
||||||
/*
|
adfs_object_fixup(dir, obj);
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -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_bigdirheader *) dir->bh_fplus[0]->b_data;
|
||||||
struct adfs_bigdirentry bde;
|
struct adfs_bigdirentry bde;
|
||||||
unsigned int offset;
|
unsigned int offset;
|
||||||
int i, ret = -ENOENT;
|
int ret = -ENOENT;
|
||||||
|
|
||||||
if (dir->pos >= le32_to_cpu(h->bigdirentries))
|
if (dir->pos >= le32_to_cpu(h->bigdirentries))
|
||||||
goto out;
|
goto out;
|
||||||
@ -193,27 +193,7 @@ adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj)
|
|||||||
offset += le32_to_cpu(bde.bigdirobnameptr);
|
offset += le32_to_cpu(bde.bigdirobnameptr);
|
||||||
|
|
||||||
dir_memcpy(dir, offset, obj->name, obj->name_len);
|
dir_memcpy(dir, offset, obj->name, obj->name_len);
|
||||||
for (i = 0; i < obj->name_len; i++)
|
adfs_object_fixup(dir, obj);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
dir->pos += 1;
|
dir->pos += 1;
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user