/* Systeme de fichiers VFAT : fonctions sur les noms de fichiers. */ /* Copyright (C) 2008 Emmanuel Varoquaux This file is part of XOS. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "vfat_collide.h" #include #include #include #include #include /* Dit si un caractere OEM non nul est valide dans le jeu de caracteres des * noms courts. */ static inline int isvalid_short(unsigned char c) { return isalnum(c) || c > 127 || strchr("$%'-_@~(){}^#&", c); } /* Dit si un caractere UNICODE non nul est valide dans le jeu de caracteres * des noms longs. */ static inline int isvalid_long(unsigned short c) { return c > 255 || isvalid_short(c) || strchr(". +,;=[]", c); } /* Decode un nom court. * * shortname doit pointer vers une zone d'au moins 11 caracteres. * buffer doit pointer vers une zone d'au moins 13 caracteres. */ static void decode_shortname(const unsigned char shortname[], unsigned char buffer[]) { int n, l, i; buffer[0] = shortname[0] == 0x05 ? 0xe5 : shortname[0]; /* 0xe5 est un caractere KANJI */ n = 1; l = shortname[0] != ' ' ? 1 : 0; i = 1; while (i < 8) if ((buffer[n++] = shortname[i++]) != ' ') l = n; if (shortname[8] != ' ') { /* extension */ buffer[n = l, n++] = '.'; while (i < 11) if ((buffer[n++] = shortname[i++]) != ' ') l = n; } buffer[l] = '\0'; } /* Algorithme de generation de la base du nom court. * * lossy_conversion indique si des caracteres invalides ont du etre remplaces. */ static void generate_basisname(const unsigned short *longname, unsigned char shortname[], int *lossy_conversion) { unsigned char *p; /* portion courante (primaire ou extension) */ int n; /* nombre de caracteres copies dans la portion courante */ unsigned short c; /* caractere courant du nom long */ /* initialisation */ *lossy_conversion = 0; /* conversion du nom long en majuscules (1) */ /* conversion des caracteres vers le jeu de caracteres 8.3 (2) */ /* suppression des espaces (3) */ /* suppression des points de tete (4) */ /* copie des caracteres dans la portion primaire du nom de base (5) */ /* copie des caracteres dans la portion de l'extension du nom de base (6, 7) */ p = shortname; n = 0; while ((c = toupper(*longname++))) { /* 1 */ if (c == ' ') /* 3 */ continue; if (p == shortname) { /* portion primaire */ if (c == '.') { if (n == 0) /* 4 */ continue; while (n < 8) /* 5 */ p[n++] = ' '; p += 8; n = 0; continue; } if (n == 8) continue; } else { /* extension, 6, 7 */ if (c == '.') { n = 0; continue; } if (n == 3) continue; } if (c > 255 || !isvalid_short(c)) { /* 2 */ c = '_'; *lossy_conversion = 1; } p[n++] = c; } while (&p[n] < shortname + 11) p[n++] = ' '; } /* Dit si le nom long est identique au nom court, c'est-a-dire s'il respecte * les conventions de nommage 8.3. */ static int fit(const unsigned short *longname, const unsigned char shortname[]) { unsigned char buf[13]; int i; decode_shortname(shortname, buf); for (i = 0; buf[i]; i++) if (*longname++ != buf[i]) return 0; return !*longname; } /* Insere "~n" a la fin de la partie principale d'un nom court. */ static void insert_numerictail(unsigned char shortname[], unsigned int n) { unsigned char buf[8]; unsigned char *p; int l; buf[0] = '~'; l = 1 + snprintf((char *)(buf + 1), 7, "%u", n); p = shortname; while (*p != ' ' && p < shortname + 8 - l) p++; memcpy(p, buf, l); } /* Insere "~n" dans le nom court, de facon a ce qu'il n'y ait pas deux noms * courts identiques dans le repertoire. * * lossy_conversion doit provenir de generate_basisname(). * Retourne 0 en cas de succes, un nombre negatif en cas d'erreur. */ static int generate_numerictail(const unsigned short *longname, unsigned char shortname[], int lossy_conversion, struct vfat_collide_fo_struct *collide_fo) { int n; unsigned char primary[8]; int rv; if (!lossy_conversion && fit(longname, shortname)) { if ((rv = collide_fo->vptr->collide(collide_fo, shortname)) < 0) return rv; if (!rv) return 0; } n = 1; memcpy(primary, shortname, 8); do { if (n > 999999) return -EINVAL; memcpy(shortname, primary, 8); insert_numerictail(shortname, n++); if ((rv = collide_fo->vptr->collide(collide_fo, shortname)) < 0) return rv; } while (rv); return 0; } /* Genere le nom long d'un fichier. * * Retourne 0 en cas de succes, un nombre negatif en cas d'erreur (nom invalide). */ static int generate_longname(const char *fs_basename, unsigned short longname[]) { int n; /* nombre de caracteres copies dans le nom long */ int spaces; /* nombre d'espaces et points consecutifs */ unsigned short c; /* caractere courant du nom */ n = 0; spaces = 0; while (c = get_fs_byte(fs_basename++), c && c != '/') { if (c == ' ') { if (n == 0) /* ignore leading spaces */ continue; spaces++; } else if (c == '.') /* leading periods are allowed */ spaces++; else if (!isvalid_long(c)) return -EINVAL; else spaces = 0; if (n > 255) return -ENAMETOOLONG; longname[n++] = c; } if (n > 255) return -ENAMETOOLONG; longname[n - spaces] = '\0'; /* ignore trailing spaces and periods */ return 0; } /* Genere le nom court d'un fichier. * * Retourne 0 en cas de succes, un nombre negatif en cas d'erreur. */ static int generate_shortname(const unsigned short *longname, unsigned char shortname[], struct vfat_collide_fo_struct *collide_fo) { int lossy_conversion; generate_basisname(longname, shortname, &lossy_conversion); return generate_numerictail(longname, shortname, lossy_conversion, collide_fo); } /* Dit si le nom de fichier concorde avec le nom long. * * basename doit pointer dans le segment fs. */ int vfat_match_long(const char *fs_basename, const unsigned short *longname) { unsigned short c; while (*longname == ' ') longname++; while (c = get_fs_byte(fs_basename++), c && c != '/') if (toupper(c) != toupper(*longname++)) return 0; while (*longname == ' ' || *longname == '.') longname++; return !*longname; } /* Dit si le nom de fichier concorde avec le nom court. * * basename doit se trouver dans le segment pointe par fs. */ int vfat_match_short(const char *fs_basename, const unsigned char shortname[]) { unsigned char buf[13]; int i; decode_shortname(shortname, buf); for (i = 0; buf[i]; i++) if (toupper((unsigned char)get_fs_byte(fs_basename++)) != toupper(buf[i])) return 0; if (!strchr((char *)buf, '.') && get_fs_byte(fs_basename) == '.') fs_basename++; /* le '.' est optionnel si le nom court n'a pas d'extension */ return !get_fs_byte(fs_basename) || get_fs_byte(fs_basename) == '/'; } /* Decode un nom long. * * Conformement a la specification, les caracteres UNICODE non traduisibles * sont remplaces par le caractere '_', et ce uniquement dans la reponse a * l'utilisateur (pas de modifications sur le disque). */ void vfat_decode_longname(const unsigned short longname[], char name[]) { int i; i = 0; do name[i++] = *longname < 256 ? *longname : '_'; while (*longname++); } /* Decode un nom court. */ void vfat_decode_shortname(const unsigned char shortname[], char name[]) { decode_shortname(shortname, (unsigned char *)name); } /* Genere les noms long et court d'un fichier. * * basename doit pointer dans le segment fs. * longname doit pointer vers une zone d'au moins 256 caracteres. * shortname doit pointer vers une zone d'au moins 11 caracteres. * Retourne 0 en cas de succes, un nombre negatif en cas d'erreur. */ int vfat_generate_names(const char *fs_basename, unsigned short longname[], unsigned char shortname[], struct vfat_collide_fo_struct *collide_fo) { int rv; if ((rv = generate_longname(fs_basename, longname)) < 0) return rv; if ((rv = generate_shortname(longname, shortname, collide_fo)) < 0) return rv; return 0; } /* Dit si le nom long est identique au nom court. Si oui, il n'est pas * necessaire de creer une entree de repertoire pour le nom long. */ int vfat_fit(const unsigned short *longname, const unsigned char shortname[]) { return fit(longname, shortname); }