Skip to content

Commit

Permalink
affs: add mount option to avoid filename truncates
Browse files Browse the repository at this point in the history
Normal behavior for filenames exceeding specific filesystem limits is to
refuse operation.

AFFS standard name length being only 30 characters against 255 for usual
Linux filesystems, original implementation does filename truncate by
default with a define value AFFS_NO_TRUNCATE which can be enabled but
needs module compilation.

This patch adds 'nofilenametruncate' mount option so that user can
easily activate that feature and avoid a lot of problems (eg overwrite
files ...)

Signed-off-by: Fabian Frederick <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
Fabian Frederick authored and torvalds committed Apr 7, 2014
1 parent d40c4d4 commit 8ca5772
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 33 deletions.
9 changes: 6 additions & 3 deletions Documentation/filesystems/affs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ mode=mode Sets the mode flags to the given (octal) value, regardless
This is useful since most of the plain AmigaOS files
will map to 600.

nofilenametruncate
The file system will return an error when filename exceeds
standard maximum filename length (30 characters).

reserved=num Sets the number of reserved blocks at the start of the
partition to num. You should never need this option.
Default is 2.
Expand Down Expand Up @@ -181,9 +185,8 @@ tested, though several hundred MB have been read and written using
this fs. For a most up-to-date list of bugs please consult
fs/affs/Changes.

Filenames are truncated to 30 characters without warning (this
can be changed by setting the compile-time option AFFS_NO_TRUNCATE
in include/linux/amigaffs.h).
By default, filenames are truncated to 30 characters without warning.
'nofilenametruncate' mount option can change that behavior.

Case is ignored by the affs in filename matching, but Linux shells
do care about the case. Example (with /wb being an affs mounted fs):
Expand Down
20 changes: 8 additions & 12 deletions fs/affs/affs.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,6 @@
#include <linux/mutex.h>
#include <linux/workqueue.h>

/* AmigaOS allows file names with up to 30 characters length.
* Names longer than that will be silently truncated. If you
* want to disallow this, comment out the following #define.
* Creating filesystem objects with longer names will then
* result in an error (ENAMETOOLONG).
*/
/*#define AFFS_NO_TRUNCATE */

/* Ugly macros make the code more pretty. */

#define GET_END_PTR(st,p,sz) ((st *)((char *)(p)+((sz)-sizeof(st))))
Expand All @@ -28,7 +20,6 @@

#define AFFS_CACHE_SIZE PAGE_SIZE

#define AFFS_MAX_PREALLOC 32
#define AFFS_LC_SIZE (AFFS_CACHE_SIZE/sizeof(u32)/2)
#define AFFS_AC_SIZE (AFFS_CACHE_SIZE/sizeof(struct affs_ext_key)/2)
#define AFFS_AC_MASK (AFFS_AC_SIZE-1)
Expand Down Expand Up @@ -118,6 +109,7 @@ struct affs_sb_info {
#define SF_OFS 0x0200 /* Old filesystem */
#define SF_PREFIX 0x0400 /* Buffer for prefix is allocated */
#define SF_VERBOSE 0x0800 /* Talk about fs when mounting */
#define SF_NO_TRUNCATE 0x1000 /* Don't truncate filenames */

/* short cut to get to the affs specific sb data */
static inline struct affs_sb_info *AFFS_SB(struct super_block *sb)
Expand All @@ -137,9 +129,13 @@ extern void affs_fix_checksum(struct super_block *sb, struct buffer_head *bh);
extern void secs_to_datestamp(time_t secs, struct affs_date *ds);
extern umode_t prot_to_mode(u32 prot);
extern void mode_to_prot(struct inode *inode);
extern void affs_error(struct super_block *sb, const char *function, const char *fmt, ...);
extern void affs_warning(struct super_block *sb, const char *function, const char *fmt, ...);
extern int affs_check_name(const unsigned char *name, int len);
extern void affs_error(struct super_block *sb, const char *function,
const char *fmt, ...);
extern void affs_warning(struct super_block *sb, const char *function,
const char *fmt, ...);
extern bool affs_nofilenametruncate(const struct dentry *dentry);
extern int affs_check_name(const unsigned char *name, int len,
bool notruncate);
extern int affs_copy_name(unsigned char *bstr, struct dentry *dentry);

/* bitmap. c */
Expand Down
23 changes: 15 additions & 8 deletions fs/affs/amigaffs.c
Original file line number Diff line number Diff line change
Expand Up @@ -471,20 +471,27 @@ affs_warning(struct super_block *sb, const char *function, const char *fmt, ...)
function,ErrorBuffer);
}

bool
affs_nofilenametruncate(const struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
return AFFS_SB(inode->i_sb)->s_flags & SF_NO_TRUNCATE;

}

/* Check if the name is valid for a affs object. */

int
affs_check_name(const unsigned char *name, int len)
affs_check_name(const unsigned char *name, int len, bool notruncate)
{
int i;

if (len > 30)
#ifdef AFFS_NO_TRUNCATE
return -ENAMETOOLONG;
#else
len = 30;
#endif

if (len > 30) {
if (notruncate)
return -ENAMETOOLONG;
else
len = 30;
}
for (i = 0; i < len; i++) {
if (name[i] < ' ' || name[i] == ':'
|| (name[i] > 0x7e && name[i] < 0xa0))
Expand Down
32 changes: 23 additions & 9 deletions fs/affs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ affs_get_toupper(struct super_block *sb)
* Note: the dentry argument is the parent dentry.
*/
static inline int
__affs_hash_dentry(struct qstr *qstr, toupper_t toupper)
__affs_hash_dentry(struct qstr *qstr, toupper_t toupper, bool notruncate)
{
const u8 *name = qstr->name;
unsigned long hash;
int i;

i = affs_check_name(qstr->name, qstr->len);
i = affs_check_name(qstr->name, qstr->len, notruncate);
if (i)
return i;

Expand All @@ -82,16 +82,22 @@ __affs_hash_dentry(struct qstr *qstr, toupper_t toupper)
static int
affs_hash_dentry(const struct dentry *dentry, struct qstr *qstr)
{
return __affs_hash_dentry(qstr, affs_toupper);
return __affs_hash_dentry(qstr, affs_toupper,
affs_nofilenametruncate(dentry));

}

static int
affs_intl_hash_dentry(const struct dentry *dentry, struct qstr *qstr)
{
return __affs_hash_dentry(qstr, affs_intl_toupper);
return __affs_hash_dentry(qstr, affs_intl_toupper,
affs_nofilenametruncate(dentry));

}

static inline int __affs_compare_dentry(unsigned int len,
const char *str, const struct qstr *name, toupper_t toupper)
const char *str, const struct qstr *name, toupper_t toupper,
bool notruncate)
{
const u8 *aname = str;
const u8 *bname = name->name;
Expand All @@ -101,7 +107,7 @@ static inline int __affs_compare_dentry(unsigned int len,
* must be valid. 'name' must be validated first.
*/

if (affs_check_name(name->name, name->len))
if (affs_check_name(name->name, name->len, notruncate))
return 1;

/*
Expand All @@ -126,13 +132,18 @@ static int
affs_compare_dentry(const struct dentry *parent, const struct dentry *dentry,
unsigned int len, const char *str, const struct qstr *name)
{
return __affs_compare_dentry(len, str, name, affs_toupper);

return __affs_compare_dentry(len, str, name, affs_toupper,
affs_nofilenametruncate(parent));
}

static int
affs_intl_compare_dentry(const struct dentry *parent, const struct dentry *dentry,
unsigned int len, const char *str, const struct qstr *name)
{
return __affs_compare_dentry(len, str, name, affs_intl_toupper);
return __affs_compare_dentry(len, str, name, affs_intl_toupper,
affs_nofilenametruncate(parent));

}

/*
Expand Down Expand Up @@ -411,7 +422,10 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry,
(u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name,
(u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name);

retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len);
retval = affs_check_name(new_dentry->d_name.name,
new_dentry->d_name.len,
affs_nofilenametruncate(old_dentry));

if (retval)
return retval;

Expand Down
6 changes: 5 additions & 1 deletion fs/affs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ static const struct super_operations affs_sops = {
};

enum {
Opt_bs, Opt_mode, Opt_mufs, Opt_prefix, Opt_protect,
Opt_bs, Opt_mode, Opt_mufs, Opt_notruncate, Opt_prefix, Opt_protect,
Opt_reserved, Opt_root, Opt_setgid, Opt_setuid,
Opt_verbose, Opt_volume, Opt_ignore, Opt_err,
};
Expand All @@ -172,6 +172,7 @@ static const match_table_t tokens = {
{Opt_bs, "bs=%u"},
{Opt_mode, "mode=%o"},
{Opt_mufs, "mufs"},
{Opt_notruncate, "nofilenametruncate"},
{Opt_prefix, "prefix=%s"},
{Opt_protect, "protect"},
{Opt_reserved, "reserved=%u"},
Expand Down Expand Up @@ -233,6 +234,9 @@ parse_options(char *options, kuid_t *uid, kgid_t *gid, int *mode, int *reserved,
case Opt_mufs:
*mount_opts |= SF_MUFS;
break;
case Opt_notruncate:
*mount_opts |= SF_NO_TRUNCATE;
break;
case Opt_prefix:
*prefix = match_strdup(&args[0]);
if (!*prefix)
Expand Down

0 comments on commit 8ca5772

Please sign in to comment.