ВНИМАНИЕ!!! Это - вторая версия. В первой обязательно будут проблемы при использовании UTF-8. Представляю вам свой очередной патч для ядра Linux, позволяющий читать и записывать файлы с русскими именами. Патч создавался на ядре 2.6.8-10 из дистрибутива Debian. Учтите что файловая система ASFS не включена в официальное ядро. Если у вас другое ядро, то файловую систему вы можете взять здесь: http://home.elka.pw.edu.pl/~mszyprow/programy/asfs/. Для активизации поддержки русского языка в аргументах файловой системы в файле /etc/fstab необходимо указать:" iocharset=koi8-r, codepage=1251". Возможно также использование iocharset=utf-8, хотя оно не тестировалось. Пожалуйста сообщите о результатах, если таковые будут. -- Best regards, Pavel Fedin, mailto:sonic_amiga@xxxxxxxxxx
--- kernel-source-2.6.8/fs/asfs/asfs_fs.h.orig 2004-11-25 00:47:24.000000000 +0300 +++ kernel-source-2.6.8/fs/asfs/asfs_fs.h 2005-02-12 02:49:13.000000000 +0300 @@ -66,6 +66,10 @@ u16 flags; char *prefix; char *root_volume; /* Volume prefix for absolute symlinks. */ + char *iocharset; + int codepage; + struct nls_table *nls_io; + struct nls_table *nls_disk; }; /* short cut to get to the asfs specific sb data */ @@ -180,8 +184,9 @@ /* namei */ u8 asfs_lowerchar(u8 c); -int asfs_namecmp(u8 *s, u8 *ct, int casesensitive); +int asfs_namecmp(u8 *s, u8 *ct, int casesensitive, struct nls_table *t); u16 asfs_hash(u8 *name, int casesensitive); +void asfs_translate(u8 *to, u8 *from, struct nls_table *nls_to, struct nls_table *nls_from); /* nodes */ int asfs_getnode(struct super_block *sb, u32 nodeno, --- kernel-source-2.6.8/fs/asfs/dir.c.orig 2004-11-25 00:47:24.000000000 +0300 +++ kernel-source-2.6.8/fs/asfs/dir.c 2005-01-07 18:56:24.000000000 +0300 @@ -29,6 +29,9 @@ { struct inode *dir = filp->f_dentry->d_inode; struct super_block *sb = dir->i_sb; + struct nls_table *nls_io = ASFS_SB(sb)->nls_io; + struct nls_table *nls_disk = ASFS_SB(sb)->nls_disk; + u8 buf[108]; unsigned long f_pos; int stored = 0; @@ -93,8 +96,9 @@ if (add && !(obj->bits & OTYPE_HIDDEN)) { unsigned int type; + asfs_translate(buf, obj->name, nls_io, nls_disk); asfs_debug("ASFS: DirFilling: entry #%d \"%s\" (node %u offset %u), type %x\n", \ - stored, obj->name, be32_to_cpu(obj->objectnode), block, obj->bits); + stored, buf, be32_to_cpu(obj->objectnode), block, obj->bits); filp->f_pos = block; if (obj->bits & OTYPE_DIR) @@ -104,7 +108,7 @@ else type = DT_REG; - if (filldir(dirent, obj->name, strlen(obj->name), block, be32_to_cpu(obj->objectnode), type) < 0) { + if (filldir(dirent, buf, strlen(buf), block, be32_to_cpu(obj->objectnode), type) < 0) { filp->private_data = (void *)be32_to_cpu(obj->objectnode); ASFS_I(dir)->modified = 0; asfs_debug("ASFS: DirFilling: to be continued...\n"); @@ -134,12 +138,15 @@ u8 *name = (u8 *) dentry->d_name.name; struct buffer_head *bh; struct fsObject *obj; + struct nls_table *nls_io = ASFS_SB(sb)->nls_io; + struct nls_table *nls_disk = ASFS_SB(sb)->nls_disk; + u8 buf[512]; asfs_debug("asfs_lookup: (searching \"%s\"...) ", name); lock_super(sb); - if (ASFS_I(dir)->hashtable != 0) { /* hashtable block is available, quick search */ + if ((!strchr(name, '?')) && (ASFS_I(dir)->hashtable != 0)) { /* hashtable block is available and name can be reverse translated, quick search */ struct fsObjectNode *node_p; struct buffer_head *node_bh; u32 node; @@ -151,7 +158,8 @@ unlock_super(sb); return ERR_PTR(res); } - hash16 = asfs_hash(name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE); + asfs_translate(buf, name, nls_disk, nls_io); + hash16 = asfs_hash(buf, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE); node = be32_to_cpu(((struct fsHashTable *) bh->b_data)->hashentry[HASHCHAIN(hash16)]); asfs_brelse(bh); @@ -173,7 +181,7 @@ node = be32_to_cpu(node_p->next); asfs_brelse(node_bh); } - } else { /* hashtable not available, long search */ + } else { /* hashtable not available or name can't be reverse-translated, long search */ struct fsObjectContainer *objcont; u32 block; --- kernel-source-2.6.8/fs/asfs/namei.c.orig 2004-11-25 00:47:24.000000000 +0300 +++ kernel-source-2.6.8/fs/asfs/namei.c 2005-01-07 18:54:10.000000000 +0300 @@ -21,6 +21,7 @@ #include <linux/buffer_head.h> #include <linux/vfs.h> #include <linux/string.h> +#include <linux/nls.h> #include "asfs_fs.h" static inline u8 asfs_upperchar(u8 c) @@ -37,6 +38,12 @@ return (c); } +static inline u8 asfs_nls_upperchar(u8 c, struct nls_table *t) +{ + u8 nc = t->charset2upper[c]; + return nc ? nc : c; +} + /* Check if the name is valid for a asfs object. */ static inline int asfs_check_name(const u8 *name, int len) @@ -61,6 +68,7 @@ const u8 *name = qstr->name; unsigned long hash; int i; + struct nls_table *nls_io = ASFS_SB(sb)->nls_io; i = asfs_check_name(qstr->name,qstr->len); if (i) @@ -73,7 +81,7 @@ hash = partial_name_hash(*name, hash); else for (i=qstr->len; i > 0; name++, i--) - hash = partial_name_hash(asfs_upperchar(*name), hash); + hash = partial_name_hash(asfs_nls_upperchar(*name, nls_io), hash); qstr->hash = end_name_hash(hash); @@ -86,6 +94,7 @@ const u8 *aname = a->name; const u8 *bname = b->name; int len; + struct nls_table *nls_io = ASFS_SB(sb)->nls_io; /* 'a' is the qstr of an already existing dentry, so the name * must be valid. 'b' must be validated first. @@ -103,7 +112,7 @@ return 1; } else { for (len=a->len; len > 0; len--) - if (asfs_upperchar(*aname++) != asfs_upperchar(*bname++)) + if (asfs_nls_upperchar(*aname++, nls_io) != asfs_nls_upperchar(*bname++, nls_io)) return 1; } @@ -115,7 +124,7 @@ d_compare: asfs_compare_dentry, }; -int asfs_namecmp(u8 *s, u8 *ct, int casesensitive) +int asfs_namecmp(u8 *s, u8 *ct, int casesensitive, struct nls_table *t) { if (casesensitive) { while (*s == *ct && *ct != '\0' && *ct != '/') { @@ -123,7 +132,7 @@ ct++; } } else { - while (asfs_upperchar(*s) == asfs_upperchar(*ct) && *ct != '\0' + while (asfs_nls_upperchar(*s, t) == asfs_nls_upperchar(*ct, t) && *ct != '\0' && *ct != '/') { s++; ct++; @@ -153,3 +162,23 @@ return hashval; } +void asfs_translate(u8 *to, u8 *from, struct nls_table *nls_to, struct nls_table *nls_from) +{ + wchar_t uni; + int i, len; + int from_len = strlen(from); + + for (i=0;i<from_len;) { + len = nls_from->char2uni(&from[i], from_len-i, &uni); + if (len > 0) { + i += len; + len = nls_to->uni2char(uni, to, NLS_MAX_CHARSET_SIZE); + if (len > 0) + to += len; + } else + i++; + if (len < 0) + *to++ = '?'; + } + *to = '\0'; +} --- kernel-source-2.6.8/fs/asfs/objects.c.orig 2004-11-25 00:47:24.000000000 +0300 +++ kernel-source-2.6.8/fs/asfs/objects.c 2005-01-08 01:17:49.000000000 +0300 @@ -39,11 +39,15 @@ struct fsObject *asfs_find_obj_by_name(struct super_block *sb, struct fsObjectContainer *objcont, u8 * name) { + struct nls_table *nls_io = ASFS_SB(sb)->nls_io; + struct nls_table *nls_disk = ASFS_SB(sb)->nls_disk; + u8 buf[512]; struct fsObject *obj; obj = &(objcont->object[0]); while (be32_to_cpu(obj->objectnode) > 0 && ((char *) obj - (char *) objcont) + sizeof(struct fsObject) + 2 < sb->s_blocksize) { - if (asfs_namecmp(obj->name, name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE) == 0) { + asfs_translate(buf, obj->name, nls_io, nls_disk); + if (asfs_namecmp(buf, name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE, nls_io) == 0) { asfs_debug("Object found! Node %u, Name %s, Type %x, inCont %u\n", be32_to_cpu(obj->objectnode), obj->name, obj->bits, be32_to_cpu(objcont->bheader.ownblock)); return obj; } @@ -201,7 +205,8 @@ static int dehashobjectquick(struct super_block *sb, u32 objectnode, u8 *name, u32 parentobjectnode) { struct fsObject *o; - int errorcode; + int errorcode = 0; + u8 buf[512]; struct buffer_head *block; asfs_debug("dehashobject: Delinking object %d (=ObjectNode) from hashchain. Parentnode = %d\n", objectnode, parentobjectnode); @@ -221,6 +226,7 @@ asfs_debug("dehashobject: Read HashTable block of parent object of object to be delinked\n"); +// asfs_translate(buf, name, ASFS_SB(sb)->nls_disk, ASFS_SB(sb)->nls_io); hashchain = HASHCHAIN(asfs_hash(name, ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE)); nexthash = be32_to_cpu(ht->hashentry[hashchain]); @@ -461,6 +467,9 @@ int errorcode; u32 object_size; u32 hashblock = be32_to_cpu((*io_o)->object.dir.hashtable); + struct nls_table *nls_io = ASFS_SB(sb)->nls_io; + struct nls_table *nls_disk = ASFS_SB(sb)->nls_disk; + char bufname[512]; asfs_debug("createobject: Creating object '%s' in dir '%s'.\n", objectname, (*io_o)->name); @@ -470,12 +479,13 @@ if (!force && be32_to_cpu((*io_o)->objectnode) == ASFS_RECYCLEDNODE) return -EINVAL; - object_size = sizeof(struct fsObject) + strlen(objectname) + 2; + asfs_translate(bufname, objectname, nls_disk, nls_io); + object_size = sizeof(struct fsObject) + strlen(bufname) + 2; if ((errorcode = findobjectspace(sb, io_bh, io_o, object_size)) == 0) { struct fsObject *o2 = *io_o; u8 *name = o2->name; - u8 *objname = objectname; + u8 *objname = bufname; struct buffer_head *node_bh; struct fsObjectNode *on; u32 nodeno; @@ -500,7 +510,7 @@ if (errorcode == 0) { /* in io_bh there is a container with created object */ on->node.data = ((struct fsBlockHeader *) (*io_bh)->b_data)->ownblock; - if ((errorcode = hashobject(sb, hashblock, on, be32_to_cpu(o2->objectnode), objectname)) == 0) { + if ((errorcode = hashobject(sb, hashblock, on, be32_to_cpu(o2->objectnode), bufname)) == 0) { asfs_bstore(sb, node_bh); asfs_brelse(node_bh); } else --- kernel-source-2.6.8/fs/asfs/super.c.orig 2004-11-25 00:47:24.000000000 +0300 +++ kernel-source-2.6.8/fs/asfs/super.c 2005-03-09 01:52:34.000000000 +0300 @@ -56,11 +56,14 @@ #include <linux/buffer_head.h> #include <linux/vfs.h> #include <linux/parser.h> +#include <linux/nls.h> #include "asfs_fs.h" #include <asm/byteorder.h> #include <asm/uaccess.h> +static char asfs_default_iocharset[] = CONFIG_ASFS_DEFAULT_IOCHARSET; + u32 asfs_calcchecksum(void *block, u32 blocksize) { u32 *data = block, checksum = 1; @@ -86,7 +89,7 @@ enum { Opt_mode, Opt_setgid, Opt_setuid, Opt_prefix, Opt_volume, - Opt_lcvol, Opt_ignore, Opt_err, + Opt_lcvol, Opt_ignore, Opt_err, Opt_iocharset, Opt_codepage }; static match_table_t tokens = { @@ -100,6 +103,8 @@ {Opt_ignore, "noquota"}, {Opt_ignore, "quota"}, {Opt_ignore, "usrquota"}, + {Opt_iocharset, "iocharset=%s"}, + {Opt_codepage, "codepage=%u"}, {Opt_err, NULL}, }; @@ -153,6 +158,17 @@ case Opt_lcvol: ASFS_SB(sb)->flags |= ASFS_VOL_LOWERCASE; break; + case Opt_iocharset: + if (ASFS_SB(sb)->iocharset != asfs_default_iocharset) + kfree(ASFS_SB(sb)->iocharset); + ASFS_SB(sb)->iocharset = match_strdup(&args[0]); + if (!ASFS_SB(sb)) + return 0; + break; + case Opt_codepage: + if (match_int(&args[0], &option)) + goto no_arg; + ASFS_SB(sb)->codepage = option; case Opt_ignore: /* Silently ignore the quota options */ break; @@ -172,6 +188,7 @@ struct buffer_head *bh; struct fsRootBlock *rootblock; struct inode *rootinode; + char buf[50]; sbi = kmalloc(sizeof(struct asfs_sb_info), GFP_KERNEL); if (!sbi) @@ -185,6 +202,8 @@ ASFS_SB(sb)->prefix = NULL; ASFS_SB(sb)->root_volume = NULL; ASFS_SB(sb)->flags = 0; + ASFS_SB(sb)->iocharset = asfs_default_iocharset; + ASFS_SB(sb)->codepage = CONFIG_ASFS_DEFAULT_CODEPAGE; if (!asfs_parse_options(data, sb)) { printk(KERN_ERR "ASFS: Error parsing options\n"); @@ -280,6 +299,23 @@ if (ASFS_SB(sb)->flags & ASFS_READONLY) sb->s_flags |= MS_RDONLY; sb->s_op = &asfs_ops; + asfs_debug("Case sensitive: "); + if (ASFS_SB(sb)->flags & ASFS_ROOTBITS_CASESENSITIVE) + asfs_debug("yes\n"); + else + asfs_debug("No\n"); + + sprintf(buf,"cp%d",ASFS_SB(sb)->codepage); + ASFS_SB(sb)->nls_disk = load_nls(buf); + if (!ASFS_SB(sb)->nls_disk) { + printk(KERN_ERR "ASFS: codepage %s not found\n", buf); + return -EINVAL; + } + ASFS_SB(sb)->nls_io = load_nls(ASFS_SB(sb)->iocharset); + if (!ASFS_SB(sb)->nls_io) { + printk(KERN_ERR "ASFS: IO charset %s not found\n", ASFS_SB(sb)->iocharset); + goto out2; + } if ((rootinode = asfs_get_root_inode(sb))) { if ((sb->s_root = d_alloc_root(rootinode))) { @@ -288,6 +324,9 @@ } iput(rootinode); } + unload_nls(ASFS_SB(sb)->nls_io); +out2: + unload_nls(ASFS_SB(sb)->nls_disk); return -EINVAL; out: @@ -327,6 +366,12 @@ kfree(ASFS_SB(sb)->prefix); if (ASFS_SB(sb)->root_volume) kfree(ASFS_SB(sb)->root_volume); + if (ASFS_SB(sb)->nls_disk) + unload_nls(ASFS_SB(sb)->nls_disk); + if (ASFS_SB(sb)->nls_io) + unload_nls(ASFS_SB(sb)->nls_io); + if (ASFS_SB(sb)->iocharset != asfs_default_iocharset) + kfree(ASFS_SB(sb)->iocharset); kfree(sbi); sb->s_fs_info = NULL; --- kernel-source-2.6.8/fs/asfs/symlink.c.orig 2004-11-25 00:47:24.000000000 +0300 +++ kernel-source-2.6.8/fs/asfs/symlink.c 2005-03-09 01:21:12.000000000 +0300 @@ -20,6 +20,7 @@ #include <linux/buffer_head.h> #include <linux/vfs.h> #include <linux/pagemap.h> +#include <linux/nls.h> #include "asfs_fs.h" #include <asm/byteorder.h> @@ -31,9 +32,13 @@ struct fsSoftLink *slinkcont; struct inode *inode = page->mapping->host; struct super_block *sb = inode->i_sb; + struct nls_table *nls_io = ASFS_SB(sb)->nls_io; + struct nls_table *nls_disk = ASFS_SB(sb)->nls_disk; char *link = kmap(page); int i = 0, j = 0; char c, lc = 0, *prefix, *lf, *p; + wchar_t uni; + int clen; if (!(bh = asfs_breadcheck(sb, ASFS_I(inode)->firstblock, ASFS_SOFTLINK_ID))) { SetPageError(page); @@ -56,12 +61,19 @@ /* adding volume prefix */ while (i < 1023 && (c = prefix[i])) link[i++] = c; - if (ASFS_SB(sb)->flags & ASFS_VOL_LOWERCASE) { - while (i < 1023 && lf[j] != ':') - link[i++] = asfs_lowerchar(lf[j++]); - } else { - while (i < 1023 && lf[j] != ':') - link[i++] = lf[j++]; + while (i < 1023 && lf[j] != ':') + { + c = lf[j++]; + if (ASFS_SB(sb)->flags & ASFS_VOL_LOWERCASE) + c = asfs_lowerchar(c); + clen = nls_disk->char2uni(&c, 1, &uni); + if (clen>0) { + clen = nls_io->uni2char(uni, &link[i], NLS_MAX_CHARSET_SIZE); + if (clen>0) + i += clen; + } + if (clen<0) + link[i++] = '?'; } if (i < 1023) link[i++] = '/'; @@ -75,8 +87,15 @@ link[i++] = '.'; link[i++] = '.'; } - link[i++] = c; lc = c; + clen = nls_disk->char2uni(&c, 1, &uni); + if (clen>0) { + clen = nls_io->uni2char(uni, &link[i], NLS_MAX_CHARSET_SIZE); + if (clen>0) + i += clen; + } + if (clen<0) + link[i++] = '?'; j++; } link[i] = '\0'; @@ -94,8 +113,12 @@ struct super_block *sb = symfile->i_sb; struct buffer_head *bh; struct fsSoftLink *slinkcont; + struct nls_table *nls_io = ASFS_SB(sb)->nls_io; + struct nls_table *nls_disk = ASFS_SB(sb)->nls_disk; char *p, c, lc; int i, maxlen, pflen; + wchar_t uni; + int clen, blen; asfs_debug("asfs_write_symlink %s to node %d\n", symname, (int)symfile->i_ino); @@ -119,10 +142,26 @@ strncmp(symname-1, ASFS_SB(sb)->prefix, (pflen = strlen(ASFS_SB(sb)->prefix))) == 0) { /* found volume prefix, ommiting it */ symname += pflen; - while ((c = *symname++) != '/' && c != '\0') { - *p++ = c; + blen = strlen(symname); + while (*symname != '/' && *symname != '\0') { + clen = nls_io->char2uni(symname, blen, &uni); + if (clen>0) { + symname += clen; + blen -= clen; + clen = nls_disk->uni2char(uni, p, NLS_MAX_CHARSET_SIZE); + if (clen>0) + p += clen; + } + else + { + symname++; + blen--; + } + if (clen<0) + *p++ = '?'; i++; } + symname++; *p++ = ':'; } else if (ASFS_SB(sb)->root_volume) { /* adding root volume name */ while (ASFS_SB(sb)->root_volume[i]) @@ -134,23 +173,46 @@ i++; } - while (i < maxlen && (c = *symname++)) { - if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') { + blen = strlen(symname); + while (i < maxlen && (c = *symname)) { + if (c == '.' && lc == '/' && symname[1] == '.' && symname[2] == '/') { *p++ = '/'; i++; - symname += 2; + symname += 3; + blen -= 3; lc = '/'; - } else if (c == '.' && lc == '/' && *symname == '/') { - symname++; + } else if (c == '.' && lc == '/' && symname[1] == '/') { + symname += 2; + blen -= 2; lc = '/'; } else { - *p++ = c; - lc = c; + clen = nls_io->char2uni(symname, blen, &uni); + if (clen>0) { + symname += clen; + blen -= clen; + clen = nls_disk->uni2char(uni, p, NLS_MAX_CHARSET_SIZE); + if (clen>0) + lc = *p; + p += clen; + } + else + { + symname++; + blen--; + } + if (clen<0) + { + *p++ = '?'; + lc = '?'; + } i++; } if (lc == '/') while (*symname == '/') + { symname++; + blen--; + } } *p = 0; --- kernel-source-2.6.8/fs/Kconfig.orig 2004-11-25 00:47:24.000000000 +0300 +++ kernel-source-2.6.8/fs/Kconfig 2005-03-09 02:01:10.072233216 +0300 @@ -1008,6 +1008,24 @@ If unsure, say N. +config ASFS_DEFAULT_CODEPAGE + int "Default codepage for SFS" + depends on ASFS_FS + default 437 + help + This option should be set to the codepage of your SFS filesystems. + It can be overridden with the 'codepage' mount option. + +config ASFS_DEFAULT_IOCHARSET + string "Default iocharset for SFS" + depends on ASFS_FS + default "iso8859-1" + help + Set this to the default I/O character set you'd like SFS to use. + It should probably match the character set that most of your + SFS filesystems use, and can be overridded with the 'iocharset' + mount option for FAT filesystems. + config ASFS_RW bool "Amiga SFS write support (DANGEROUS)" depends on ASFS_FS --- kernel-source-2.6.8/Documentation/filesystems/asfs.txt.orig 2004-11-25 00:47:24.000000000 +0300 +++ kernel-source-2.6.8/Documentation/filesystems/asfs.txt 2005-03-09 01:42:54.000000000 +0300 @@ -76,6 +76,14 @@ Translate all volume names in symlinks to lower case. Disabled by default. (See below.) +iocharset=name + Character set to use for converting file names. Specifies character + set used by your Linux system. + +hfscodepage=### + Set the codepage number for converting file names. Specifies + character set used by your Amiga. + Symbolic links ==============