affs: add mount option to avoid filename truncates
authorFabian Frederick <fabf@skynet.be>
Mon, 7 Apr 2014 22:39:01 +0000 (15:39 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 7 Apr 2014 23:36:08 +0000 (16:36 -0700)
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 <fabf@skynet.be>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Documentation/filesystems/affs.txt
fs/affs/affs.h
fs/affs/amigaffs.c
fs/affs/namei.c
fs/affs/super.c

index 81ac488e375802d14864e2b9a57d826334c4eb08..71b63c2b98410cd9d657b9876897fcdb65189076 100644 (file)
@@ -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.
@@ -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):
index 3952121f2f28beee731618ae915a433f16e16fa0..25b23b1e7f22ead9bd5d3d6d5fbd7e6aa2755ce1 100644 (file)
@@ -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))))
@@ -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)
@@ -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)
@@ -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 */
index d9a43674cb9479eb673140121e60ee0355acf409..533a322c41c02ffd8b1dfffb98b3f1b60593e757 100644 (file)
@@ -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))
index c36cbb4537a26e0348a44f0ec7a65f64ac3204bf..6dae1ccd176d60446f45ff8c35d24c1a17e34f13 100644 (file)
@@ -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;
 
@@ -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;
@@ -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;
 
        /*
@@ -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));
+
 }
 
 /*
@@ -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;
 
index 4fad16adbe7b24fdb964d2f81ecb39c8b58c9f06..6d589f28bf9b849bae629cea4d61f7dba60eda2d 100644 (file)
@@ -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,
 };
@@ -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"},
@@ -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)