home *** CD-ROM | disk | FTP | other *** search
- /*
- * file_maps.c: routines for handling mapping between unix and dos file names.
- *
- * This file is part of the nfsfixd program. See the file COPYRIGHT for
- * copyright information.
- *
- * Craig Barratt, Sun Feb 20 16:15:19 PST 1994
- */
-
- /*
- * system include files
- */
- #include <ctype.h>
- #include <malloc.h>
- #include <memory.h>
- #include <rpc/rpc.h>
- #include <stdio.h>
- #include <string.h>
- #include <time.h>
-
- /*
- * local include files (nfs_prot.h must be before config.h)
- */
- #include <nfs_prot.h>
- #include <config.h>
- #include <hash_table.h>
- #include <file_map.h>
-
- #define MIN(a,b) ((a) < (b) ? (a) : (b))
- #define MAX(a,b) ((a) > (b) ? (a) : (b))
-
- /*
- * Hash table for filename mappings on per directory basis (key is the
- * directory's nfs file handle, and data is a filename mapping structure.)
- * Each entry in this structure is a DirMapInfo structure.
- */
- static HashTable *MapHT = (HashTable*)NULL;
- #define MAP_HT_Z 507 /* directory hash table size */
- #define FILE_HT_Z 61 /* file hash table size (for each dir) */
-
- extern void d_printf(char *fmt, ...);
-
- /*
- * A boolean array indexed by an unsigned char. Non-zero if char is
- * a legal dos filename char, zero otherwise.
- */
- static char DosOk[256];
-
- static void dosOk_init(void)
- {
- int i;
-
- if ( DosOk['a'] ) return;
- for ( i = 0 ; i < 256 ; i++ ) {
- DosOk[i] = islower(i) || isdigit(i);
- }
- DosOk['_'] = 1;
- DosOk['^'] = 1;
- DosOk['$'] = 1;
- DosOk['~'] = 1;
- DosOk['!'] = 1;
- DosOk['#'] = 1;
- DosOk['%'] = 1;
- DosOk['&'] = 1;
- DosOk['-'] = 1;
- DosOk['{'] = 1;
- DosOk['}'] = 1;
- DosOk['('] = 1;
- DosOk[')'] = 1;
- DosOk['@'] = 1;
- DosOk['`'] = 1;
- DosOk['\''] = 1;
- }
-
- /*
- * Checks if s is a valid dos (8.3) file name (ie: lower case alphanumeric
- * or special chars, at most one period, at most 8 chars before period,
- * and at most 3 chars after period.
- */
- static int is_dosOk(char *s)
- {
- char *period = strrchr(s, '.');
-
- if ( !DosOk['a'] )
- dosOk_init();
- if ( period ) {
- if ( !strcmp(s, ".") || !strcmp(s, "..") )
- return 1;
- if ( period == s || period - s > 8 || strlen(period) > 4 || !period[1] )
- return 0;
- } else {
- if ( strlen(s) > 8 )
- return 0;
- }
- for ( ; *s ; s++ ) {
- if ( s != period && !DosOk[(unsigned char)*s] )
- return 0;
- }
- return 1;
- }
-
- static void dos_sanitize(char *dos, char *prefix, char *suffix)
- {
- int i, j;
-
- for ( i = 0 ; prefix[i] ; i++ ) {
- dos[i] = tolower(prefix[i]);
- if ( !DosOk[(unsigned)dos[i]] )
- dos[i] = '-';
- }
- for ( j = 0 ; suffix[j] ; j++, i++ ) {
- dos[i] = tolower(suffix[j]);
- if ( j > 0 && !DosOk[(unsigned)dos[i]] )
- dos[i] = '-';
- }
- dos[i] = '\0';
- }
-
- char *file_add_d2u(DirMapInfo *dmp, char *dosName)
- {
- char unixName[64];
- char *period, *unixNameDup, *dosNameDup, *ret;
- int i;
-
- if ( ret = hash_find(dmp->d2u, dosName, strlen(dosName)) ) {
- d_printf("file_add_d2u: returning %s <--> %s\n", ret, dosName);
- return ret;
- }
- if ( !strcmp(dosName, ".") || !strcmp(dosName, "..") ) {
- strcpy(unixName, dosName);
- goto insert;
- }
- /*
- * We need to find a unix file name that isn't already taken.
- *
- * Case 1: try exactly the dos file name
- */
- strcpy(unixName, dosName);
- if ( !hash_find(dmp->u2d, unixName, strlen(unixName)) )
- goto insert;
- /*
- * Case 2: we need to append the prefix of the file name with some
- * unique integer.
- */
- if ( !(period = strchr(dosName, '.')) )
- period = dosName + strlen(dosName);
- for ( i = 0 ; i < 1000000 ; i++ ) {
- char digits[10];
-
- sprintf(digits, "%d", i);
- strncpy(unixName, dosName, period - dosName);
- strcpy(unixName + (period - dosName), digits);
- strcat(unixName, period);
- if ( !hash_find(dmp->u2d, unixName, strlen(unixName)) )
- goto insert;
- }
- fprintf(stderr, "file_add_d2u: can't find unique dos file for %s\n",
- unixName);
- return dosName;
- insert:
- /*
- * add the correspondence unixName <--> dosName
- */
- d_printf("file_add_d2u: adding %s <--> %s\n", unixName, dosName);
- if ( !(unixNameDup = strdup(unixName)) || !(dosNameDup = strdup(dosName)) ) {
- fprintf(stderr, "file_add_d2u: can't allocate string space\n");
- return dosName;
- }
- if ( hash_insert(dmp->u2d, unixNameDup, strlen(unixNameDup), dosNameDup) )
- fprintf(stderr, "file_add_d2u: can't insert into u2d hash table\n");
- if ( hash_insert(dmp->d2u, dosNameDup, strlen(dosNameDup), unixNameDup) )
- fprintf(stderr, "file_add_d2u: can't insert into d2u hash table\n");
- return unixNameDup;
- }
-
- char *file_add_u2d(DirMapInfo *dmp, char *unixName)
- {
- char prefix[16], suffix[16], dosName[16];
- char *period = strrchr(unixName, '.');
- char *unixNameDup, *dosNameDup;
- int i, len, preLen;
- char *ret;
-
- if ( ret = hash_find(dmp->u2d, unixName, strlen(unixName)) ) {
- d_printf("file_add_u2d: returning %s <--> %s\n", unixName, ret);
- return ret;
- }
- if ( !strcmp(unixName, ".") || !strcmp(unixName, "..") ) {
- strcpy(dosName, unixName);
- goto insert;
- }
- if ( period == unixName )
- period = (char*)NULL;
- if ( period ) {
- strncpy(suffix, period, 4);
- suffix[4] = '\0';
- } else {
- suffix[0] = '\0';
- }
-
- /*
- * We need to find a dos file name that isn't already taken.
- *
- * Case 1: first try the first 8 chars of the prefix cast to lower
- */
- len = period ? period - unixName : strlen(unixName);
- preLen = MIN(8, len);
- strncpy(prefix, unixName, preLen);
- prefix[preLen] = '\0';
- dos_sanitize(dosName, prefix, suffix);
- if ( !hash_find(dmp->d2u, dosName, strlen(dosName)) )
- goto insert;
-
- if ( len > 8 ) {
- /*
- * Case 2: see if there are trailing digits that we can use
- */
- for ( i = 0 ; i < len && isdigit(unixName[len - 1 - i]) ; i++ ) ;
- if ( 0 < i && i < 8 ) {
- strncpy(prefix, unixName, 8 - i);
- strncpy(prefix + 8 - i, unixName + len - i, i);
- prefix[8] = '\0';
- dos_sanitize(dosName, prefix, suffix);
- if ( !hash_find(dmp->d2u, dosName, strlen(dosName)) )
- goto insert;
- }
- /*
- * Case 3: see if we can ignore some chars in the middle of the
- * unix file name.
- */
- for ( i = 7 ; i > 0 ; i-- ) {
- strncpy(prefix, unixName, i);
- strncpy(prefix + i, unixName + len - 8 + i, 8 - i);
- prefix[8] = '\0';
- dos_sanitize(dosName, prefix, suffix);
- if ( !hash_find(dmp->d2u, dosName, strlen(dosName)) )
- goto insert;
- }
- }
- /*
- * Subsets of the unix file name didn't give a unique dos file.
- *
- * Case 4: we need to append or overwrite trailing parts of the
- * prefix with some unique integers.
- */
- strncpy(prefix, unixName, preLen);
- prefix[preLen] = '\0';
- for ( i = 0 ; i < 1000000 ; i++ ) {
- char digits[10];
- int digLen;
-
- sprintf(digits, "%d", i);
- digLen = strlen(digits);
- strcpy(prefix + preLen - MAX(0, digLen + preLen - 8), digits);
- dos_sanitize(dosName, prefix, suffix);
- if ( !hash_find(dmp->d2u, dosName, strlen(dosName)) )
- goto insert;
- }
- fprintf(stderr, "file_add_u2d: can't find unique dos file for %s\n",
- unixName);
- return unixName;
- insert:
- /*
- * add the correspondence unixName <--> dosName
- */
- d_printf("file_add_u2d: adding %s <--> %s\n", unixName, dosName);
- if ( !(unixNameDup = strdup(unixName)) || !(dosNameDup = strdup(dosName)) ) {
- fprintf(stderr, "file_add_u2d: can't allocate string space\n");
- return unixName;
- }
- if ( hash_insert(dmp->u2d, unixNameDup, strlen(unixNameDup), dosNameDup) )
- fprintf(stderr, "file_add_u2d: can't insert into u2d hash table\n");
- if ( hash_insert(dmp->d2u, dosNameDup, strlen(dosNameDup), unixNameDup) )
- fprintf(stderr, "file_add_u2d: can't insert into d2u hash table\n");
- return dosNameDup;
- }
-
- void file_updatedir(DirMapInfo *dmp, CLIENT *nfsRPC)
- {
- readdirargs rd;
- readdirres *rds;
-
- /*
- * first add all the filenames that are already legal dos filenames
- */
- bzero(&rd.cookie, sizeof(rd.cookie));
- rd.dir = dmp->fh;
- rd.count = NFS_MAXDATA;
- do {
- entry *p;
-
- if ( !(rds = nfsproc_readdir_2(&rd, nfsRPC))
- || rds->status != NFS_OK ) {
- fprintf(stderr, "file_updatedir: RPC nfs readdir call failed\n");
- break;
- }
- for ( p = rds->readdirres_u.reply.entries ; p ; p = p->nextentry ) {
- if ( !p->nextentry )
- memcpy(&rd.cookie, &p->cookie, sizeof(rd.cookie));
- if ( !is_dosOk(p->name) )
- continue;
- file_add_u2d(dmp, p->name);
- }
- } while ( !rds->readdirres_u.reply.eof );
- /*
- * next add all the filenames that are not legal dos filenames
- */
- bzero(&rd.cookie, sizeof(rd.cookie));
- rd.dir = dmp->fh;
- rd.count = NFS_MAXDATA;
- do {
- entry *p;
-
- if ( !(rds = nfsproc_readdir_2(&rd, nfsRPC))
- || rds->status != NFS_OK ) {
- fprintf(stderr, "file_updatedir: RPC nfs readdir call failed\n");
- break;
- }
- for ( p = rds->readdirres_u.reply.entries ; p ; p = p->nextentry ) {
- if ( !p->nextentry )
- memcpy(&rd.cookie, &p->cookie, sizeof(rd.cookie));
- if ( is_dosOk(p->name) )
- continue;
- file_add_u2d(dmp, p->name);
- }
- } while ( !rds->readdirres_u.reply.eof );
- }
-
- DirMapInfo *file_get_mapinfo(nfs_fh *fh, CLIENT *nfsRPC)
- {
- DirMapInfo *dmp;
-
- if ( !MapHT && !(MapHT = hash_new(MAP_HT_Z)) ) {
- fprintf(stderr, "file_get_mapinfo: can't create dir hash table\n");
- return (DirMapInfo*)NULL;
- }
- if ( dmp = (DirMapInfo*)hash_find(MapHT, (char*)fh, sizeof(*fh)) ) {
- dmp->lastUsage = time((time_t*)NULL);
- return dmp;
- }
- /*
- * Create a new DirMapInfo structure and insert the contents
- * of the unix directory.
- */
- if ( !(dmp = (DirMapInfo*)malloc(sizeof(DirMapInfo))) ) {
- fprintf(stderr, "file_get_mapinfo: can't allocate memory\n");
- return (DirMapInfo*)NULL;
- }
- dmp->lastUsage = time((time_t*)NULL);
- dmp->fh = *fh;
- if ( !(dmp->d2u = hash_new(FILE_HT_Z)) || !(dmp->u2d = hash_new(FILE_HT_Z)) ) {
- fprintf(stderr, "file_get_mapinfo: can't create file map hash tables\n");
- free(dmp);
- return (DirMapInfo*)NULL;
- }
- if ( hash_insert(MapHT, (char*)&dmp->fh, sizeof(dmp->fh), dmp) ) {
- fprintf(stderr, "file_get_mapinfo: can't insert into hash table\n");
- exit(1);
- }
- file_updatedir(dmp, nfsRPC);
- return dmp;
- }
-
- /*
- * Given a unix file name, and the directory's nfs file handle,
- * return the corresponding dos file name.
- */
- char *file_unix2dos(nfs_fh *fh, char *unixName, CLIENT *nfsRPC)
- {
- #ifdef FIX_FILENAMES
- DirMapInfo *dmp = file_get_mapinfo(fh, nfsRPC);
-
- if ( !dmp ) return unixName;
- return file_add_u2d(dmp, unixName);
- #else
- return unixName;
- #endif
- }
-
- /*
- * Given a dos file name, and the directory's nfs file handle,
- * return the corresponding unix file name.
- */
- char *file_dos2unix(nfs_fh *fh, char *dosName, CLIENT *nfsRPC)
- {
- #ifdef FIX_FILENAMES
- DirMapInfo *dmp = file_get_mapinfo(fh, nfsRPC);
- static char lowerDosName[16];
- int i;
-
- for ( i = 0 ; dosName[i] && i < 15 ; i++ )
- lowerDosName[i] = tolower(dosName[i]);
- lowerDosName[i] = '\0';
- if ( !dmp ) return lowerDosName;
- return file_add_d2u(dmp, lowerDosName);
- #else
- return dosName;
- #endif
- }
-
- /*
- * called by the hash_loopall function in file_destroy_map_entry()
- */
- static int file_destroy_u2d_entry(HashTable *ht, HashEntry *entry, void *arg)
- {
- free(entry->name);
- free(entry->data);
- return 0;
- }
-
- /*
- * called by the hash_loopall function in file_map_destroy_all()
- */
- static int file_destroy_map_entry(HashTable *ht, HashEntry *entry, void *arg)
- {
- DirMapInfo *dmp = (DirMapInfo*)entry->data;
- /*
- * Free the unix and dos names in the u2d hash table. The same pointers
- * are used in the d2u hash table, so we don't need to free d2u entries.
- */
- hash_loopall(dmp->u2d, file_destroy_u2d_entry, NULL);
- /*
- * Free everything else
- */
- hash_free(dmp->u2d);
- hash_free(dmp->d2u);
- free(dmp);
- hash_delete_entry(MapHT, entry);
- return 0;
- }
-
- /*
- * Given a directory's file handle, destroy the file map info
- */
- void file_destroy_map(nfs_fh *fh)
- {
- HashEntry *entry;
-
- if ( !MapHT || !(entry = hash_find_entry(MapHT, (char*)fh, sizeof(*fh))) )
- return;
- file_destroy_map_entry(MapHT, entry, NULL);
- }
-
- /*
- * Destroy all the file map info
- */
- void file_destroy_all_maps(void)
- {
- if ( !MapHT )
- return;
- hash_loopall(MapHT, file_destroy_map_entry, NULL);
- }
-
- /*
- * called by the hash_loopall function in file_destroy_old_maps()
- */
- static int file_destroy_old_map_entry(HashTable *ht, HashEntry *entry,
- time_t *oldest)
- {
- DirMapInfo *dmp = (DirMapInfo*)entry->data;
- if ( dmp->lastUsage < *oldest ) {
- d_printf("file_destroy_old_map_entry: destroying old directory map\n");
- return file_destroy_map_entry(ht, entry, NULL);
- } else {
- return 0;
- }
- }
-
- /*
- * Destroy old file map info (anything with a lastUsage more than
- * ago seconds ago).
- */
- void file_destroy_old_maps(int age)
- {
- time_t oldest = time((time_t*)NULL) - age;
-
- if ( !MapHT )
- return;
- d_printf("file_destroy_old_maps: checking for old directory maps\n");
- hash_loopall(MapHT, file_destroy_old_map_entry, &oldest);
- }
-