From mboxrd@z Thu Jan 1 00:00:00 1970 From: Ryan Lortie Date: Wed, 23 May 2007 14:18:55 +0000 Subject: Re: support for vfat long filenames Message-Id: <1179929935.6119.5.camel@moonpix.desrt.ca> MIME-Version: 1 Content-Type: multipart/mixed; boundary="=-y8yUecNjH3oAmTCz1X77" List-Id: References: <1179900959.20773.3.camel@moonpix.desrt.ca> In-Reply-To: <1179900959.20773.3.camel@moonpix.desrt.ca> To: linux-hotplug@vger.kernel.org --=-y8yUecNjH3oAmTCz1X77 Content-Type: text/plain Content-Transfer-Encoding: 7bit On Wed, 2007-23-05 at 08:42 +0200, Kay Sievers wrote: > On 5/23/07, Ryan Lortie wrote: > > 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... > > Oh, does that mean that fat can have labels with long filenames? What > tool can create these long labels, can you set such labels with > Windows now? i've been using the mtools package (specifically, "mlabel") to test my work. it supports long labels in exactly the way you would expect. desrt@moonpix:~$ sudo mdir -i /dev/sdc2 :: Volume in drive : is My iPod has a very long name! (abbr=MY IPOD HAS) i don't know about windows since i don't have access to a windows box, but i assume it's true there too....? > Can you use volume_id_set_unicode16() in utils.c, or is that different? ah. i didn't know about this. done :) > And please don't use assert(), just return no label if something goes wrong. done -- this part is reworked a little to use memcpy anyway. also fixed some small indentation and { placement issues. thanks for the swift review. cheers --=-y8yUecNjH3oAmTCz1X77 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 10:16:50.000000000 -0400 @@ -34,6 +34,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 +98,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 +110,110 @@ 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; + +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) +{ + uint8_t buffer[VFAT_LFN_SEQ_MAX*VFAT_LFN_CHARS_PER_ENTRY*2]; + 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; + + 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 -- should never happen */ + if (len + sizeof lfn->name0 + sizeof sizeof lfn->name1 + + sizeof lfn->name2 > sizeof buffer) + break; + + memcpy (&buffer[len], lfn->name0, sizeof lfn->name0); + len += sizeof lfn->name0; + memcpy (&buffer[len], lfn->name1, sizeof lfn->name1); + len += sizeof lfn->name1; + memcpy (&buffer[len], lfn->name2, sizeof lfn->name2); + len += sizeof lfn->name2; + + if (lfn->seq & VFAT_LFN_SEQ_LAST) { + if (len > 510) + len = 510; + + volume_id_set_unicode16 ((char *) filename, 3*255, + buffer, LE, len); + + 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 +239,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 +397,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 +480,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); --=-y8yUecNjH3oAmTCz1X77 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/ --=-y8yUecNjH3oAmTCz1X77 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 --=-y8yUecNjH3oAmTCz1X77--