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:
Linus Torvalds 2019-06-06 11:02:54 -07:00
commit 44e843eb5c
4 changed files with 93 additions and 133 deletions

View File

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

View File

@ -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;
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 = {

View File

@ -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);
}
/*

View File

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