AFFS almost always uses latin1. However usual iocharset by now is UTF-8. So NLS support is required. Signed-off-by: Vladimir Serbinenko <phcoder@xxxxxxxxx> diff --git a/fs/affs/Kconfig b/fs/affs/Kconfig index cfad9af..93ab9d4b 100644 --- a/fs/affs/Kconfig +++ b/fs/affs/Kconfig @@ -19,3 +19,11 @@ config AFFS_FS To compile this file system support as a module, choose M here: the module will be called affs. If unsure, say N. + +config AFFS_DEFAULT_CODEPAGE + string "Default codepage for AFFS" + depends on AFFS_FS + default "iso8859-1" + help + This option should be set to the codepage of your AFFS filesystems. + It can be overridden with the "codepage" mount option. diff --git a/fs/affs/affs.h b/fs/affs/affs.h index fc1d4ca..7bf696d 100644 --- a/fs/affs/affs.h +++ b/fs/affs/affs.h @@ -12,6 +12,8 @@ */ /*#define AFFS_NO_TRUNCATE */ +#define AFFS_MAXNAME 30 + /* Ugly macros make the code more pretty. */ #define GET_END_PTR(st,p,sz) ((st *)((char *)(p)+((sz)-sizeof(st)))) @@ -107,6 +109,10 @@ struct affs_sb_info { u32 s_bmap_bits; /* # of bits in one bitmap blocks */ u32 s_last_bmap; struct buffer_head *s_bmap_bh; + char *iocharset; + char *codepage; + struct nls_table *nls_io; + struct nls_table *nls_disk; char *s_prefix; /* Prefix for volumes and assigns. */ char s_volume[32]; /* Volume prefix for absolute symlinks. */ spinlock_t symlink_lock; /* protects the previous two */ @@ -142,8 +148,9 @@ 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 int affs_copy_name(unsigned char *bstr, struct dentry *dentry); +extern int affs_check_name(const unsigned char *name, unsigned int len); +extern int affs_copy_name(struct super_block *sb, unsigned char *bstr, + struct dentry *dentry); /* bitmap. c */ @@ -165,8 +172,13 @@ extern int affs_link(struct dentry *olddentry, struct inode *dir, struct dentry *dentry); extern int affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname); +extern int affs_read_symlink(struct inode *inode, + char *link); extern int affs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry); +extern size_t affs_translate(u8 *to, const u8 *from, struct nls_table *nls_to, + struct nls_table *nls_from, size_t limit, + size_t from_len); /* inode.c */ diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c index 52a6407..08f39ee 100644 --- a/fs/affs/amigaffs.c +++ b/fs/affs/amigaffs.c @@ -477,15 +477,15 @@ affs_warning(struct super_block *sb, const char *function, const char *fmt, ...) /* 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, unsigned int len) { int i; - if (len > 30) + if (len > AFFS_MAXNAME) #ifdef AFFS_NO_TRUNCATE return -ENAMETOOLONG; #else - len = 30; + len = AFFS_MAXNAME; #endif for (i = 0; i < len; i++) { @@ -497,19 +497,3 @@ affs_check_name(const unsigned char *name, int len) return 0; } -/* This function copies name to bstr, with at most 30 - * characters length. The bstr will be prepended by - * a length byte. - * NOTE: The name will must be already checked by - * affs_check_name()! - */ - -int -affs_copy_name(unsigned char *bstr, struct dentry *dentry) -{ - int len = min(dentry->d_name.len, 30u); - - *bstr++ = len; - memcpy(bstr, dentry->d_name.name, len); - return len; -} diff --git a/fs/affs/dir.c b/fs/affs/dir.c index 8ca8f3a..81da2ec 100644 --- a/fs/affs/dir.c +++ b/fs/affs/dir.c @@ -55,6 +55,8 @@ affs_readdir(struct file *filp, void *dirent, filldir_t filldir) u32 ino; int stored; int res; + struct nls_table *nls_io = AFFS_SB(sb)->nls_io; + struct nls_table *nls_disk = AFFS_SB(sb)->nls_disk; pr_debug("AFFS: readdir(ino=%lu,f_pos=%lx)\n",inode->i_ino,(unsigned long)filp->f_pos); @@ -122,17 +124,31 @@ affs_readdir(struct file *filp, void *dirent, filldir_t filldir) f_pos = (hash_pos << 16) + 2; inside: do { + /* AFFS names are at most 30 characters and are all in + BMP. So at most 90 characters resulting translation + assuming UTF-8 as iocharset. Then comes paranoia. + In worst-case scenario filenames get truncated but + no overflow occurs. + */ + char buf[200]; + size_t translatedlen; fh_bh = affs_bread(sb, ino); if (!fh_bh) { affs_error(sb, "readdir","Cannot read block %d", ino); goto readdir_done; } - namelen = min(AFFS_TAIL(sb, fh_bh)->name[0], (u8)30); + namelen = min(((u8 *)AFFS_TAIL(sb, fh_bh)->name)[0], + (u8)AFFS_MAXNAME); name = AFFS_TAIL(sb, fh_bh)->name + 1; + translatedlen = affs_translate(buf, name, + nls_io, nls_disk, + sizeof(buf), + namelen); pr_debug("AFFS: readdir(): filldir(\"%.*s\", ino=%u), hash=%d, f_pos=%x\n", namelen, name, ino, hash_pos, f_pos); - if (filldir(dirent, name, namelen, f_pos, ino, DT_UNKNOWN) < 0) + if (filldir(dirent, buf, translatedlen, f_pos, ino, + DT_UNKNOWN) < 0) goto readdir_done; stored++; f_pos++; diff --git a/fs/affs/inode.c b/fs/affs/inode.c index 88a4b0b..2a88f7a 100644 --- a/fs/affs/inode.c +++ b/fs/affs/inode.c @@ -143,11 +143,17 @@ struct inode *affs_iget(struct super_block *sb, unsigned long ino) inode->i_fop = &affs_file_operations; break; case ST_SOFTLINK: + { + int s; inode->i_mode |= S_IFLNK; inode->i_op = &affs_symlink_inode_operations; inode->i_data.a_ops = &affs_symlink_aops; + s = affs_read_symlink(inode, 0); + + inode->i_size = s >= 0 ? s : 0; break; } + } inode->i_mtime.tv_sec = inode->i_atime.tv_sec = inode->i_ctime.tv_sec = (be32_to_cpu(tail->change.days) * (24 * 60 * 60) + @@ -375,7 +381,7 @@ affs_add_entry(struct inode *dir, struct inode *inode, struct dentry *dentry, s3 AFFS_HEAD(bh)->ptype = cpu_to_be32(T_SHORT); AFFS_HEAD(bh)->key = cpu_to_be32(bh->b_blocknr); - affs_copy_name(AFFS_TAIL(sb, bh)->name, dentry); + affs_copy_name(sb, AFFS_TAIL(sb, bh)->name, dentry); AFFS_TAIL(sb, bh)->stype = cpu_to_be32(type); AFFS_TAIL(sb, bh)->parent = cpu_to_be32(dir->i_ino); diff --git a/fs/affs/namei.c b/fs/affs/namei.c index 4780694..600fa3e 100644 --- a/fs/affs/namei.c +++ b/fs/affs/namei.c @@ -9,6 +9,7 @@ */ #include "affs.h" +#include <linux/nls.h> typedef int (*toupper_t)(int); @@ -66,18 +67,24 @@ 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 super_block *sb, struct qstr *qstr, toupper_t toupper) { - const u8 *name = qstr->name; + const u8 *name; unsigned long hash; int i; + u8 tmp[AFFS_MAXNAME + 1]; + size_t len; - i = affs_check_name(qstr->name, qstr->len); + len = affs_translate(tmp, qstr->name, AFFS_SB(sb)->nls_disk, + AFFS_SB(sb)->nls_io, sizeof(tmp), qstr->len); + + i = affs_check_name(tmp, len); if (i) return i; hash = init_name_hash(); - i = min(qstr->len, 30u); + i = len; + name = tmp; for (; i > 0; name++, i--) hash = partial_name_hash(toupper(*name), hash); qstr->hash = end_name_hash(hash); @@ -89,41 +96,49 @@ static int affs_hash_dentry(const struct dentry *dentry, const struct inode *inode, struct qstr *qstr) { - return __affs_hash_dentry(qstr, affs_toupper); + struct super_block *sb = inode->i_sb; + return __affs_hash_dentry(sb, qstr, affs_toupper); } static int affs_intl_hash_dentry(const struct dentry *dentry, const struct inode *inode, struct qstr *qstr) { - return __affs_hash_dentry(qstr, affs_intl_toupper); + struct super_block *sb = inode->i_sb; + return __affs_hash_dentry(sb, qstr, affs_intl_toupper); } -static inline int __affs_compare_dentry(unsigned int len, - const char *str, const struct qstr *name, toupper_t toupper) +static inline int __affs_compare_dentry(struct super_block *sb, + unsigned int len, + const char *str, + const struct qstr *name, + toupper_t toupper) { - const u8 *aname = str; - const u8 *bname = name->name; + u8 atmp[AFFS_MAXNAME + 1], btmp[AFFS_MAXNAME + 1]; + size_t alen, blen; + const u8 *aname = atmp; + const u8 *bname = btmp; + + alen = affs_translate(atmp, str, AFFS_SB(sb)->nls_disk, + AFFS_SB(sb)->nls_io, sizeof(atmp), len); + blen = affs_translate(btmp, name->name, AFFS_SB(sb)->nls_disk, + AFFS_SB(sb)->nls_io, sizeof(btmp), name->len); /* * 'str' is the name of an already existing dentry, so the name * must be valid. 'name' must be validated first. */ - if (affs_check_name(name->name, name->len)) + if (affs_check_name(bname, blen)) return 1; /* * If the names are longer than the allowed 30 chars, * the excess is ignored, so their length may differ. */ - if (len >= 30) { - if (name->len < 30) - return 1; - len = 30; - } else if (len != name->len) + if (alen != blen) return 1; - for (; len > 0; len--) + for (; alen > 0; alen--) if (toupper(*aname++) != toupper(*bname++)) return 1; @@ -135,14 +150,16 @@ affs_compare_dentry(const struct dentry *parent, const struct inode *pinode, const struct dentry *dentry, const struct inode *inode, unsigned int len, const char *str, const struct qstr *name) { - return __affs_compare_dentry(len, str, name, affs_toupper); + struct super_block *sb = pinode->i_sb; + return __affs_compare_dentry(sb, len, str, name, affs_toupper); } static int affs_intl_compare_dentry(const struct dentry *parent,const struct inode *pinode, const struct dentry *dentry, const struct inode *inode, unsigned int len, const char *str, const struct qstr *name) { - return __affs_compare_dentry(len, str, name, affs_intl_toupper); + struct super_block *sb = pinode->i_sb; + return __affs_compare_dentry(sb, len, str, name, affs_intl_toupper); } /* @@ -150,15 +167,12 @@ affs_intl_compare_dentry(const struct dentry *parent,const struct inode *pinode, */ static inline int -affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper) +affs_match(const u8 *name, size_t len, const u8 *name2, toupper_t toupper) { - const u8 *name = dentry->d_name.name; - int len = dentry->d_name.len; - - if (len >= 30) { - if (*name2 < 30) + if (len >= AFFS_MAXNAME) { + if (*name2 < AFFS_MAXNAME) return 0; - len = 30; + len = AFFS_MAXNAME; } else if (len != *name2) return 0; @@ -172,9 +186,12 @@ int affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len) { toupper_t toupper = affs_get_toupper(sb); - int hash; + uint32_t hash; + + if (len > AFFS_MAXNAME) + len = AFFS_MAXNAME; - hash = len = min(len, 30u); + hash = len; for (; len > 0; len--) hash = (hash * 13 + toupper(*name++)) & 0x7ff; @@ -188,6 +205,12 @@ affs_find_entry(struct inode *dir, struct dentry *dentry) struct buffer_head *bh; toupper_t toupper = affs_get_toupper(sb); u32 key; + u8 name[AFFS_MAXNAME + 1]; + size_t namelen; + + namelen = affs_translate(name, dentry->d_name.name, + AFFS_SB(sb)->nls_disk, AFFS_SB(sb)->nls_io, + sizeof(name), dentry->d_name.len); pr_debug("AFFS: find_entry(\"%.*s\")\n", (int)dentry->d_name.len, dentry->d_name.name); @@ -195,7 +218,8 @@ affs_find_entry(struct inode *dir, struct dentry *dentry) if (!bh) return ERR_PTR(-EIO); - key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]); + key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, + name, namelen)]); for (;;) { affs_brelse(bh); @@ -204,7 +228,7 @@ affs_find_entry(struct inode *dir, struct dentry *dentry) bh = affs_bread(sb, key); if (!bh) return ERR_PTR(-EIO); - if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper)) + if (affs_match(name, namelen, AFFS_TAIL(sb, bh)->name, toupper)) return bh; key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain); } @@ -332,9 +356,14 @@ affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) char *p; int i, maxlen, error; char c, lc; + char convbuf[10]; + size_t convbufpos = 0, convbuflen = 0; + struct nls_table *nls_io = AFFS_SB(sb)->nls_io; + struct nls_table *nls_disk = AFFS_SB(sb)->nls_disk; + const char *symnameend = symname + strlen(symname); - pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino, - (int)dentry->d_name.len,dentry->d_name.name,symname); + pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n", dir->i_ino, + (int)dentry->d_name.len, dentry->d_name.name, symname); maxlen = AFFS_SB(sb)->s_hashsize * sizeof(u32) - 1; inode = affs_new_inode(dir); @@ -362,7 +391,44 @@ affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) *p++ = sbi->s_volume[i++]; spin_unlock(&sbi->symlink_lock); } - while (i < maxlen && (c = *symname++)) { + + while (i < maxlen) { + if (convbuflen == convbufpos) { + convbufpos = convbuflen = 0; + if (*symname == 0) + break; + if (nls_disk && nls_io) { + ssize_t len; + wchar_t uni; + len = nls_io->char2uni(symname, + symnameend - symname, + &uni); + if (len <= 0) { + convbufpos = 0; + convbuflen = 1; + convbuf[0] = '?'; + symname++; + } else { + symname += len; + len = nls_disk->uni2char(uni, convbuf, + sizeof(convbuf) + ); + if (len > 0) { + convbuflen = len; + } else { + convbufpos = 0; + convbuflen = 1; + convbuf[0] = '?'; + } + } + } else { + convbufpos = 0; + convbuflen = 1; + convbuf[0] = *symname++; + } + } + c = convbuf[convbufpos++]; + if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') { *p++ = '/'; i++; @@ -381,6 +447,9 @@ affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) symname++; } *p = 0; + + inode->i_size = affs_read_symlink(inode, 0); + mark_buffer_dirty_inode(bh, inode); affs_brelse(bh); mark_inode_dirty(inode); @@ -444,7 +513,7 @@ affs_rename(struct inode *old_dir, struct dentry *old_dentry, goto done; /* And insert it into the new directory with the new name. */ - affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry); + affs_copy_name(sb, AFFS_TAIL(sb, bh)->name, new_dentry); affs_fix_checksum(sb, bh); affs_lock_dir(new_dir); retval = affs_insert_hash(new_dir, bh); @@ -456,3 +525,71 @@ done: affs_brelse(bh); return retval; } + +static size_t affs_translate_real(u8 *to, const u8 *from, + struct nls_table *nls_to, + struct nls_table *nls_from, + size_t limit, size_t from_len) +{ + wchar_t uni; + size_t i; + ssize_t len; + size_t to_len = limit; + u8 *to0 = to; + + if (nls_to) { + for (i = 0; i < from_len && to_len > 0 && from[i]; ) { + len = nls_from->char2uni(&from[i], from_len-i, &uni); + if (len > 0) { + i += len; + len = nls_to->uni2char(uni, to, to_len); + if (len > 0) { + to += len; + to_len -= len; + } + } else + i++; + if (len <= 0) { + *to++ = '?'; + to_len--; + } + } + return to - to0; + } else { + size_t len; + len = from_len; + if (len > limit) + len = limit; + memcpy(to, from, len); + return len; + } +} + +size_t affs_translate(u8 *to, const u8 *from, struct nls_table *nls_to, + struct nls_table *nls_from, size_t limit, size_t from_len) +{ + size_t r; + r = affs_translate_real(to, from, nls_to, nls_from, + limit - 1, from_len); + to[r] = 0; + return r; +} +/* This function copies name to bstr, with at most AFFS_MAXNAME + * characters length. The bstr will be prepended by + * a length byte. + * NOTE: The name will must be already checked by + * affs_check_name()! + */ + +int +affs_copy_name(struct super_block *sb, unsigned char *bstr, + struct dentry *dentry) +{ + size_t len; + len = affs_translate_real(bstr + 1, dentry->d_name.name, + AFFS_SB(sb)->nls_disk, + AFFS_SB(sb)->nls_io, AFFS_MAXNAME, + dentry->d_name.len); + *bstr = len; + return len; +} diff --git a/fs/affs/super.c b/fs/affs/super.c index 1df3c95..27f9e98 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -17,10 +17,18 @@ #include <linux/magic.h> #include <linux/sched.h> #include <linux/slab.h> +#include <linux/nls.h> #include "affs.h" extern struct timezone sys_tz; +#ifdef CONFIG_AFFS_DEFAULT_CODEPAGE +static char affs_default_codepage[] = CONFIG_AFFS_DEFAULT_CODEPAGE; +#else +static char affs_default_codepage[] = "iso8859-1"; +#endif +static char affs_default_iocharset[] = CONFIG_NLS_DEFAULT; + static int affs_statfs(struct dentry *dentry, struct kstatfs *buf); static int affs_remount (struct super_block *sb, int *flags, char *data); @@ -48,6 +56,15 @@ affs_put_super(struct super_block *sb) if (!(sb->s_flags & MS_RDONLY) && sb->s_dirt) affs_commit_super(sb, 1, 1); + if (sbi->nls_disk) + unload_nls(sbi->nls_disk); + if (sbi->nls_io) + unload_nls(sbi->nls_io); + if (sbi->iocharset != affs_default_iocharset) + kfree(sbi->iocharset); + if (sbi->codepage != affs_default_codepage) + kfree(sbi->codepage); + kfree(sbi->s_prefix); affs_free_bitmap(sb); affs_brelse(sbi->s_root_bh); @@ -149,7 +166,8 @@ static const struct super_operations affs_sops = { enum { Opt_bs, Opt_mode, Opt_mufs, Opt_prefix, Opt_protect, Opt_reserved, Opt_root, Opt_setgid, Opt_setuid, - Opt_verbose, Opt_volume, Opt_ignore, Opt_err, + Opt_verbose, Opt_volume, Opt_iocharset, Opt_codepage, + Opt_ignore, Opt_err, }; static const match_table_t tokens = { @@ -168,12 +186,16 @@ static const match_table_t tokens = { {Opt_ignore, "noquota"}, {Opt_ignore, "quota"}, {Opt_ignore, "usrquota"}, + {Opt_iocharset, "iocharset=%s"}, + {Opt_codepage, "codepage=%s"}, {Opt_err, NULL}, }; static int parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s32 *root, - int *blocksize, char **prefix, char *volume, unsigned long *mount_opts) + int *blocksize, char **prefix, char *volume, + unsigned long *mount_opts, + char **iocharset, char **codepage) { char *p; substring_t args[MAX_OPT_ARGS]; @@ -246,6 +268,24 @@ parse_options(char *options, uid_t *uid, gid_t *gid, int *mode, int *reserved, s *uid = option; *mount_opts |= SF_SETUID; break; + case Opt_iocharset: + if (*iocharset != affs_default_iocharset) { + kfree(*iocharset); + *iocharset = NULL; + } + *iocharset = match_strdup(&args[0]); + if (!*iocharset) + return 0; + break; + case Opt_codepage: + if (*codepage != affs_default_codepage) { + kfree(*codepage); + *codepage = NULL; + } + *codepage = match_strdup(&args[0]); + if (!*codepage) + return 0; + break; case Opt_verbose: *mount_opts |= SF_VERBOSE; break; @@ -309,14 +349,53 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent) mutex_init(&sbi->s_bmlock); spin_lock_init(&sbi->symlink_lock); + sbi->iocharset = affs_default_iocharset; + sbi->codepage = affs_default_codepage; + if (!parse_options(data,&uid,&gid,&i,&reserved,&root_block, - &blocksize,&sbi->s_prefix, - sbi->s_volume, &mount_flags)) { + &blocksize, &sbi->s_prefix, + sbi->s_volume, &mount_flags, + &sbi->iocharset, &sbi->codepage + )) { printk(KERN_ERR "AFFS: Error parsing options\n"); kfree(sbi->s_prefix); + if (sbi->iocharset != affs_default_iocharset) + kfree(sbi->iocharset); + if (sbi->codepage != affs_default_codepage) + kfree(sbi->codepage); kfree(sbi); return -EINVAL; } + + if (sbi->codepage[0] != '\0' && strcmp(sbi->codepage, "none") != 0) { + sbi->nls_disk = load_nls(sbi->codepage); + if (!sbi->nls_disk) { + printk(KERN_ERR "AFFS: codepage %s not found\n", + sbi->codepage); + if (sbi->iocharset != affs_default_iocharset) + kfree(sbi->iocharset); + if (sbi->codepage != affs_default_codepage) + kfree(sbi->codepage); + kfree(sbi); + return -EINVAL; + } + sbi->nls_io = load_nls(sbi->iocharset); + if (!sbi->nls_io) { + printk(KERN_ERR "AFFS: IO charset %s not found\n", + sbi->iocharset); + unload_nls(sbi->nls_disk); + if (sbi->iocharset != affs_default_iocharset) + kfree(sbi->iocharset); + if (sbi->codepage != affs_default_codepage) + kfree(sbi->codepage); + kfree(sbi); + return -EINVAL; + } + } else { + sbi->nls_io = NULL; + sbi->nls_disk = NULL; + } + /* N.B. after this point s_prefix must be released */ sbi->s_flags = mount_flags; @@ -449,7 +528,7 @@ got_root: if (mount_flags & SF_VERBOSE) { u8 len = AFFS_ROOT_TAIL(sb, root_bh)->disk_name[0]; printk(KERN_NOTICE "AFFS: Mounting volume \"%.*s\": Type=%.3s\\%c, Blocksize=%d\n", - len > 31 ? 31 : len, + len > (AFFS_MAXNAME + 1) ? (AFFS_MAXNAME + 1) : len, AFFS_ROOT_TAIL(sb, root_bh)->disk_name + 1, sig, sig[3] + '0', blocksize); } @@ -474,7 +553,7 @@ got_root: root_inode = affs_iget(sb, root_block); if (IS_ERR(root_inode)) { ret = PTR_ERR(root_inode); - goto out_error; + goto out_error_noinode; } if (AFFS_SB(sb)->s_flags & SF_INTL) @@ -495,6 +574,18 @@ got_root: * Begin the cascaded cleanup ... */ out_error: + if (root_inode) + iput(root_inode); +out_error_noinode: + if (sbi->nls_disk) + unload_nls(sbi->nls_disk); + if (sbi->nls_io) + unload_nls(sbi->nls_io); + if (sbi->iocharset != affs_default_iocharset) + kfree(sbi->iocharset); + if (sbi->codepage != affs_default_codepage) + kfree(sbi->codepage); + kfree(sbi->s_bitmap); affs_brelse(root_bh); kfree(sbi->s_prefix); @@ -526,7 +617,8 @@ affs_remount(struct super_block *sb, int *flags, char *data) memcpy(volume, sbi->s_volume, 32); if (!parse_options(data, &uid, &gid, &mode, &reserved, &root_block, &blocksize, &prefix, volume, - &mount_flags)) { + &mount_flags, + &sbi->iocharset, &sbi->codepage)) { kfree(prefix); kfree(new_opts); return -EINVAL; @@ -577,7 +669,7 @@ affs_statfs(struct dentry *dentry, struct kstatfs *buf) buf->f_bavail = free; buf->f_fsid.val[0] = (u32)id; buf->f_fsid.val[1] = (u32)(id >> 32); - buf->f_namelen = 30; + buf->f_namelen = AFFS_MAXNAME; return 0; } diff --git a/fs/affs/symlink.c b/fs/affs/symlink.c index ee00f08..be94d01 100644 --- a/fs/affs/symlink.c +++ b/fs/affs/symlink.c @@ -9,17 +9,21 @@ */ #include "affs.h" +#include <linux/nls.h> -static int affs_symlink_readpage(struct file *file, struct page *page) +int affs_read_symlink(struct inode *inode, char *link) { struct buffer_head *bh; - struct inode *inode = page->mapping->host; - char *link = kmap(page); struct slink_front *lf; int err; int i, j; char c; char lc; + const char *symname, *symnameend; + char convbuf[10]; + size_t convbufpos = 0, convbuflen = 0; + struct nls_table *nls_io = AFFS_SB(inode->i_sb)->nls_io; + struct nls_table *nls_disk = AFFS_SB(inode->i_sb)->nls_disk; pr_debug("AFFS: follow_link(ino=%lu)\n",inode->i_ino); @@ -37,36 +41,97 @@ static int affs_symlink_readpage(struct file *file, struct page *page) char *pf; spin_lock(&sbi->symlink_lock); pf = sbi->s_prefix ? sbi->s_prefix : "/"; - while (i < 1023 && (c = pf[i])) - link[i++] = c; + while (i < 1023 && (c = pf[i])) { + if (link) + link[i] = c; + i++; + } spin_unlock(&sbi->symlink_lock); while (i < 1023 && lf->symname[j] != ':') link[i++] = lf->symname[j++]; + if (i < 1023 && link) + link[i] = '/'; if (i < 1023) - link[i++] = '/'; + i++; j++; lc = '/'; } - while (i < 1023 && (c = lf->symname[j])) { + symname = lf->symname + j; + symnameend = symname + strlen(symname); + while (i < 1023) { + pr_debug("Remaining <%s>\n", symname); + if (convbuflen == convbufpos) { + convbufpos = convbuflen = 0; + if (*symname == 0) + break; + if (nls_disk && nls_io) { + ssize_t len; + wchar_t uni; + len = nls_disk->char2uni(symname, + symnameend - symname, + &uni); + if (len <= 0) { + convbufpos = 0; + convbuflen = 1; + convbuf[0] = '?'; + symname++; + } else { + symname += len; + len = nls_io->uni2char(uni, convbuf, + sizeof(convbuf)); + if (len > 0) { + convbuflen = len; + } else { + convbufpos = 0; + convbuflen = 1; + convbuf[0] = '?'; + } + } + } else { + convbufpos = 0; + convbuflen = 1; + convbuf[0] = *symname++; + } + } + c = convbuf[convbufpos++]; + pr_debug("Fetching char <%c>\n", c); + if (c == '/' && lc == '/' && i < 1020) { /* parent dir */ - link[i++] = '.'; - link[i++] = '.'; + if (link) { + link[i] = '.'; + link[i + 1] = '.'; + } + i += 2; } - link[i++] = c; + if (link) + link[i] = c; + i++; lc = c; - j++; } - link[i] = '\0'; + if (link) + link[i] = '\0'; affs_brelse(bh); + return i; +fail: + return err; +} + +static int affs_symlink_readpage(struct file *file, struct page *page) +{ + struct inode *inode = page->mapping->host; + char *link = kmap(page); + int ret; + ret = affs_read_symlink(inode, link); + if (ret < 0) { + SetPageError(page); + kunmap(page); + unlock_page(page); + return ret; + } SetPageUptodate(page); kunmap(page); unlock_page(page); return 0; -fail: - SetPageError(page); - kunmap(page); - unlock_page(page); - return err; } const struct address_space_operations affs_symlink_aops = {
Attachment:
signature.asc
Description: OpenPGP digital signature