From mboxrd@z Thu Jan 1 00:00:00 1970 From: Ryan Lortie Date: Wed, 23 May 2007 06:15:59 +0000 Subject: support for vfat long filenames Message-Id: <1179900959.20773.3.camel@moonpix.desrt.ca> MIME-Version: 1 Content-Type: multipart/mixed; boundary="=-dNNjHvCOp0FdHnZ4C3aq" List-Id: To: linux-hotplug@vger.kernel.org --=-dNNjHvCOp0FdHnZ4C3aq Content-Type: text/plain Content-Transfer-Encoding: 7bit hello. here's a first go at a patch to implement long filename support for vfat in libvolume_id. i'm not on the list, so please keep me cc:'d in on any discussion... cheers --=-dNNjHvCOp0FdHnZ4C3aq Content-Disposition: attachment; filename=vfat-lfn.patch Content-Type: text/x-patch; name=vfat-lfn.patch; charset=UTF-8 Content-Transfer-Encoding: 7bit --- a/extras/volume_id/lib/fat.c 2007-03-28 14:47:26.000000000 -0400 +++ b/extras/volume_id/lib/fat.c 2007-05-23 02:12:42.000000000 -0400 @@ -16,6 +16,7 @@ # include #endif +#include #include #include #include @@ -34,6 +35,16 @@ #define FAT_ATTR_MASK 0x3f #define FAT_ENTRY_FREE 0xe5 +#define VFAT_LFN_SEQ_MASK 0x3f +#define VFAT_LFN_SEQ_LAST 0x40 +#define VFAT_LFN_SEQ_MAX 20 +#define VFAT_LFN_CHARS_PER_ENTRY (5 + 6 + 2) +#define VFAT_LOWERCASE_NAME 0x10 +#define VFAT_LOWERCASE_EXT 0x08 + +#define TRUE 1 +#define FALSE 0 + struct vfat_super_block { uint8_t boot_jump[3]; uint8_t sysid[8]; @@ -88,9 +99,10 @@ struct vfat_dir_entry { uint8_t name[11]; uint8_t attr; + uint8_t lowercase; + uint8_t fine_time_creat; uint16_t time_creat; uint16_t date_creat; - uint16_t time_acc; uint16_t date_acc; uint16_t cluster_high; uint16_t time_write; @@ -99,6 +111,139 @@ uint32_t size; } PACKED; + +struct vfat_lfn_entry { + uint8_t seq; + uint16_t name0[5]; + uint8_t attr; + uint8_t reserved; + uint8_t cksum; + uint16_t name1[6]; + uint16_t cluster; + uint16_t name2[2]; +} PACKED; + +/* output must be at least 3 times longer than input */ +static void +utf8_from_ucs16 (uint8_t *utf8, uint16_t *ucs16) +{ + while (*ucs16) { + if (*ucs16 & 0xf800) { + /* three-byte encoding */ + *utf8++ = 0xe0 | ((*ucs16 >> 12) & 0x0f); + *utf8++ = 0x80 | ((*ucs16 >> 6) & 0x3f); + *utf8++ = 0x80 | ((*ucs16 >> 0) & 0x3f); + } else if (*ucs16 & 0xff80) { + /* two-byte encoding */ + *utf8++ = 0xc0 | ((*ucs16 >> 6) & 0x1f); + *utf8++ = 0x80 | ((*ucs16 >> 0) & 0x3f); + } else { + /* ascii */ + *utf8++ = *ucs16; + } + + ucs16++; + } +} + +static uint8_t +fat_lfn_checksum (const uint8_t name[11]) +{ + uint8_t cksum = 0; + int i; + + /* http://en.wikipedia.org/wiki/File_Allocation_Table */ + for (i = 0; i < 11; i++) + cksum = ((cksum & 1) ? 0x80 : 0) + (cksum >> 1) + name[i]; + + return cksum; +} + +static int +fat_read_lfn (struct vfat_dir_entry *dir, + struct vfat_dir_entry *entry, + uint8_t *filename) +{ + uint16_t buffer[VFAT_LFN_SEQ_MAX*VFAT_LFN_CHARS_PER_ENTRY]; + uint8_t expected_seq = 1; + uint8_t cksum; + int len = 0; + + cksum = fat_lfn_checksum (entry->name); + + while (--entry >= dir) + { + struct vfat_lfn_entry *lfn = (struct vfat_lfn_entry *) entry; + unsigned int i; + + if (expected_seq > VFAT_LFN_SEQ_MAX) + break; + + if ((lfn->attr & FAT_ATTR_MASK) != FAT_ATTR_LONG_NAME) + break; + + if (lfn->cksum != cksum) + break; + + if ((lfn->seq & VFAT_LFN_SEQ_MASK) != expected_seq++) + break; + + if (lfn->cluster != 0) + break; + + /* extra paranoia */ + assert (((char *) &buffer[len]) - ((char *) &buffer[0]) + + sizeof lfn->name0 + + sizeof lfn->name1 + + sizeof lfn->name2 <= sizeof buffer); + + for (i = 0; i < sizeof lfn->name0 / sizeof (uint16_t); i++) + buffer[len++] = le16_to_cpu (lfn->name0[i]); + for (i = 0; i < sizeof lfn->name1 / sizeof (uint16_t); i++) + buffer[len++] = le16_to_cpu (lfn->name1[i]); + for (i = 0; i < sizeof lfn->name2 / sizeof (uint16_t); i++) + buffer[len++] = le16_to_cpu (lfn->name2[i]); + + if (lfn->seq & VFAT_LFN_SEQ_LAST) + { + /* length is 255 including null terminator */ + buffer[255] = '\0'; + + if (expected_seq <= VFAT_LFN_SEQ_MAX) + buffer[len] = '\0'; + + utf8_from_ucs16 (filename, buffer); + + return TRUE; + } + } + + return FALSE; +} + +static uint8_t * +fat_read_filename (struct vfat_dir_entry *dir, struct vfat_dir_entry *entry) +{ + static uint8_t filename[3*255]; + int i; + + /* check if maybe we have LFN entries */ + if (fat_read_lfn (dir, entry, filename)) + return filename; + + /* else, read the normal 8.3 name */ + for (i = 0; i < 11; i++) { + if (entry->lowercase & ((i < 8) ? VFAT_LOWERCASE_NAME : + VFAT_LOWERCASE_EXT)) + filename[i] = tolower (entry->name[i]); + else + filename[i] = entry->name[i]; + } + + return filename; +} + + static uint8_t *get_attr_volume_id(struct vfat_dir_entry *dir, unsigned int count) { unsigned int i; @@ -124,7 +269,7 @@ continue; dbg("found ATTR_VOLUME_ID id in root dir"); - return dir[i].name; + return fat_read_filename (dir, &dir[i]); } dbg("skip dir entry"); @@ -282,8 +427,13 @@ return -1; if (label != NULL && memcmp(label, "NO NAME ", 11) != 0) { - volume_id_set_label_raw(id, label, 11); - volume_id_set_label_string(id, label, 11); + int length = strlen ((char *) label); + + if (length > 64) + length = 64; + + volume_id_set_label_raw(id, label, length); + volume_id_set_label_string(id, label, length); } else if (memcmp(vs->type.fat.label, "NO NAME ", 11) != 0) { volume_id_set_label_raw(id, vs->type.fat.label, 11); volume_id_set_label_string(id, vs->type.fat.label, 11); @@ -360,8 +510,13 @@ return -1; if (label != NULL && memcmp(label, "NO NAME ", 11) != 0) { - volume_id_set_label_raw(id, label, 11); - volume_id_set_label_string(id, label, 11); + int length = strlen ((char *) label); + + if (length > 64) + length = 64; + + volume_id_set_label_raw(id, label, length); + volume_id_set_label_string(id, label, length); } else if (memcmp(vs->type.fat32.label, "NO NAME ", 11) != 0) { volume_id_set_label_raw(id, vs->type.fat32.label, 11); volume_id_set_label_string(id, vs->type.fat32.label, 11); --=-dNNjHvCOp0FdHnZ4C3aq Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline ------------------------------------------------------------------------- This SF.net email is sponsored by DB2 Express Download DB2 Express C - the FREE version of DB2 express and take control of your XML. No limits. Just data. Click to get it now. http://sourceforge.net/powerbar/db2/ --=-dNNjHvCOp0FdHnZ4C3aq Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ Linux-hotplug-devel mailing list http://linux-hotplug.sourceforge.net Linux-hotplug-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/linux-hotplug-devel --=-dNNjHvCOp0FdHnZ4C3aq--