home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume42 / nfsfixd / part01 / file_map.c next >
Encoding:
C/C++ Source or Header  |  1994-04-24  |  12.9 KB  |  482 lines

  1. /*
  2.  * file_maps.c: routines for handling mapping between unix and dos file names.
  3.  *
  4.  * This file is part of the nfsfixd program.  See the file COPYRIGHT for
  5.  * copyright information.
  6.  *
  7.  * Craig Barratt, Sun Feb 20 16:15:19 PST 1994
  8.  */
  9.  
  10. /*
  11.  * system include files
  12.  */
  13. #include <ctype.h>
  14. #include <malloc.h>
  15. #include <memory.h>
  16. #include <rpc/rpc.h>
  17. #include <stdio.h>
  18. #include <string.h>
  19. #include <time.h>
  20.  
  21. /*
  22.  * local include files (nfs_prot.h must be before config.h)
  23.  */
  24. #include <nfs_prot.h>
  25. #include <config.h>
  26. #include <hash_table.h>
  27. #include <file_map.h>
  28.  
  29. #define    MIN(a,b)    ((a) < (b) ? (a) : (b))
  30. #define    MAX(a,b)    ((a) > (b) ? (a) : (b))
  31.  
  32. /*
  33.  * Hash table for filename mappings on per directory basis (key is the
  34.  * directory's nfs file handle, and data is a filename mapping structure.)
  35.  * Each entry in this structure is a DirMapInfo structure.
  36.  */
  37. static HashTable *MapHT = (HashTable*)NULL;
  38. #define    MAP_HT_Z    507    /* directory hash table size */
  39. #define    FILE_HT_Z    61    /* file hash table size (for each dir) */
  40.  
  41. extern void d_printf(char *fmt, ...);
  42.  
  43. /*
  44.  * A boolean array indexed by an unsigned char.  Non-zero if char is
  45.  * a legal dos filename char, zero otherwise.
  46.  */
  47. static char DosOk[256];
  48.  
  49. static void dosOk_init(void)
  50. {
  51.     int i;
  52.  
  53.     if ( DosOk['a'] ) return;
  54.     for ( i = 0 ; i < 256 ; i++ ) {
  55.     DosOk[i] = islower(i) || isdigit(i);
  56.     }
  57.     DosOk['_'] = 1;
  58.     DosOk['^'] = 1;
  59.     DosOk['$'] = 1;
  60.     DosOk['~'] = 1;
  61.     DosOk['!'] = 1;
  62.     DosOk['#'] = 1;
  63.     DosOk['%'] = 1;
  64.     DosOk['&'] = 1;
  65.     DosOk['-'] = 1;
  66.     DosOk['{'] = 1;
  67.     DosOk['}'] = 1;
  68.     DosOk['('] = 1;
  69.     DosOk[')'] = 1;
  70.     DosOk['@'] = 1;
  71.     DosOk['`'] = 1;
  72.     DosOk['\''] = 1;
  73. }
  74.  
  75. /*
  76.  * Checks if s is a valid dos (8.3) file name (ie: lower case alphanumeric
  77.  * or special chars, at most one period, at most 8 chars before period,
  78.  * and at most 3 chars after period.
  79.  */
  80. static int is_dosOk(char *s)
  81. {
  82.     char *period = strrchr(s, '.');
  83.  
  84.     if ( !DosOk['a'] )
  85.     dosOk_init();
  86.     if ( period ) {
  87.     if ( !strcmp(s, ".") || !strcmp(s, "..") )
  88.         return 1;
  89.         if ( period == s || period - s > 8 || strlen(period) > 4 || !period[1] )
  90.         return 0;
  91.     } else {
  92.     if ( strlen(s) > 8 )
  93.         return 0;
  94.     }
  95.     for ( ; *s ; s++ ) {
  96.     if ( s != period && !DosOk[(unsigned char)*s] )
  97.         return 0;
  98.     }
  99.     return 1;
  100. }
  101.  
  102. static void dos_sanitize(char *dos, char *prefix, char *suffix)
  103. {
  104.     int i, j;
  105.  
  106.     for ( i = 0 ; prefix[i] ; i++ ) {
  107.     dos[i] = tolower(prefix[i]);
  108.     if ( !DosOk[(unsigned)dos[i]] )
  109.         dos[i] = '-';
  110.     }
  111.     for ( j = 0 ; suffix[j] ; j++, i++ ) {
  112.     dos[i] = tolower(suffix[j]);
  113.     if ( j > 0 && !DosOk[(unsigned)dos[i]] )
  114.         dos[i] = '-';
  115.     }
  116.     dos[i] = '\0';
  117. }
  118.  
  119. char *file_add_d2u(DirMapInfo *dmp, char *dosName)
  120. {
  121.     char unixName[64];
  122.     char *period, *unixNameDup, *dosNameDup, *ret;
  123.     int i;
  124.  
  125.     if ( ret = hash_find(dmp->d2u, dosName, strlen(dosName)) ) {
  126.     d_printf("file_add_d2u: returning %s <--> %s\n", ret, dosName);
  127.     return ret;
  128.     }
  129.     if ( !strcmp(dosName, ".") || !strcmp(dosName, "..") ) {
  130.     strcpy(unixName, dosName);
  131.     goto insert;
  132.     }
  133.     /*
  134.      * We need to find a unix file name that isn't already taken.
  135.      *
  136.      * Case 1: try exactly the dos file name
  137.      */
  138.     strcpy(unixName, dosName);
  139.     if ( !hash_find(dmp->u2d, unixName, strlen(unixName)) )
  140.     goto insert;
  141.     /*
  142.      * Case 2: we need to append the prefix of the file name with some
  143.      * unique integer.
  144.      */
  145.     if ( !(period = strchr(dosName, '.')) )
  146.     period = dosName + strlen(dosName);
  147.     for ( i = 0 ; i < 1000000 ; i++ ) {
  148.         char digits[10];
  149.  
  150.     sprintf(digits, "%d", i);
  151.     strncpy(unixName, dosName, period - dosName);
  152.     strcpy(unixName + (period - dosName), digits);
  153.     strcat(unixName, period);
  154.     if ( !hash_find(dmp->u2d, unixName, strlen(unixName)) )
  155.         goto insert;
  156.     }
  157.     fprintf(stderr, "file_add_d2u: can't find unique dos file for %s\n",
  158.                         unixName);
  159.     return dosName;
  160. insert:
  161.     /*
  162.      * add the correspondence unixName <--> dosName
  163.      */
  164.     d_printf("file_add_d2u: adding %s <--> %s\n", unixName, dosName);
  165.     if ( !(unixNameDup = strdup(unixName)) || !(dosNameDup = strdup(dosName)) ) {
  166.     fprintf(stderr, "file_add_d2u: can't allocate string space\n");
  167.     return dosName;
  168.     }
  169.     if ( hash_insert(dmp->u2d, unixNameDup, strlen(unixNameDup), dosNameDup) )
  170.     fprintf(stderr, "file_add_d2u: can't insert into u2d hash table\n");
  171.     if ( hash_insert(dmp->d2u, dosNameDup, strlen(dosNameDup), unixNameDup) )
  172.     fprintf(stderr, "file_add_d2u: can't insert into d2u hash table\n");
  173.     return unixNameDup;
  174. }
  175.  
  176. char *file_add_u2d(DirMapInfo *dmp, char *unixName)
  177. {
  178.     char prefix[16], suffix[16], dosName[16];
  179.     char *period = strrchr(unixName, '.');
  180.     char *unixNameDup, *dosNameDup;
  181.     int i, len, preLen;
  182.     char *ret;
  183.  
  184.     if ( ret = hash_find(dmp->u2d, unixName, strlen(unixName)) ) {
  185.     d_printf("file_add_u2d: returning %s <--> %s\n", unixName, ret);
  186.     return ret;
  187.     }
  188.     if ( !strcmp(unixName, ".") || !strcmp(unixName, "..") ) {
  189.     strcpy(dosName, unixName);
  190.     goto insert;
  191.     }
  192.     if ( period == unixName )
  193.     period = (char*)NULL;
  194.     if ( period ) {
  195.     strncpy(suffix, period, 4);
  196.     suffix[4] = '\0';
  197.     } else {
  198.     suffix[0] = '\0';
  199.     }
  200.  
  201.     /*
  202.      * We need to find a dos file name that isn't already taken.
  203.      *
  204.      * Case 1: first try the first 8 chars of the prefix cast to lower
  205.      */
  206.     len = period ? period - unixName : strlen(unixName);
  207.     preLen = MIN(8, len);
  208.     strncpy(prefix, unixName, preLen);
  209.     prefix[preLen] = '\0';
  210.     dos_sanitize(dosName, prefix, suffix);
  211.     if ( !hash_find(dmp->d2u, dosName, strlen(dosName)) )
  212.     goto insert;
  213.  
  214.     if ( len > 8 ) {
  215.     /*
  216.      * Case 2: see if there are trailing digits that we can use
  217.      */
  218.     for ( i = 0 ; i < len && isdigit(unixName[len - 1 - i]) ; i++ ) ;
  219.     if ( 0 < i && i < 8 ) {
  220.         strncpy(prefix, unixName, 8 - i);
  221.         strncpy(prefix + 8 - i, unixName + len - i, i);
  222.         prefix[8] = '\0';
  223.         dos_sanitize(dosName, prefix, suffix);
  224.         if ( !hash_find(dmp->d2u, dosName, strlen(dosName)) )
  225.         goto insert;
  226.     }
  227.     /*
  228.      * Case 3: see if we can ignore some chars in the middle of the
  229.      * unix file name.
  230.      */
  231.     for ( i = 7 ; i > 0 ; i-- ) {
  232.         strncpy(prefix, unixName, i);
  233.         strncpy(prefix + i, unixName + len - 8 + i, 8 - i);
  234.         prefix[8] = '\0';
  235.         dos_sanitize(dosName, prefix, suffix);
  236.         if ( !hash_find(dmp->d2u, dosName, strlen(dosName)) )
  237.         goto insert;
  238.     }
  239.     }
  240.     /*
  241.      * Subsets of the unix file name didn't give a unique dos file.
  242.      *
  243.      * Case 4: we need to append or overwrite trailing parts of the
  244.      * prefix with some unique integers.
  245.      */
  246.     strncpy(prefix, unixName, preLen);
  247.     prefix[preLen] = '\0';
  248.     for ( i = 0 ; i < 1000000 ; i++ ) {
  249.         char digits[10];
  250.     int digLen;
  251.  
  252.     sprintf(digits, "%d", i);
  253.     digLen = strlen(digits);
  254.     strcpy(prefix + preLen - MAX(0, digLen + preLen - 8), digits);
  255.     dos_sanitize(dosName, prefix, suffix);
  256.     if ( !hash_find(dmp->d2u, dosName, strlen(dosName)) )
  257.         goto insert;
  258.     }
  259.     fprintf(stderr, "file_add_u2d: can't find unique dos file for %s\n",
  260.                         unixName);
  261.     return unixName;
  262. insert:
  263.     /*
  264.      * add the correspondence unixName <--> dosName
  265.      */
  266.     d_printf("file_add_u2d: adding %s <--> %s\n", unixName, dosName);
  267.     if ( !(unixNameDup = strdup(unixName)) || !(dosNameDup = strdup(dosName)) ) {
  268.     fprintf(stderr, "file_add_u2d: can't allocate string space\n");
  269.     return unixName;
  270.     }
  271.     if ( hash_insert(dmp->u2d, unixNameDup, strlen(unixNameDup), dosNameDup) )
  272.     fprintf(stderr, "file_add_u2d: can't insert into u2d hash table\n");
  273.     if ( hash_insert(dmp->d2u, dosNameDup, strlen(dosNameDup), unixNameDup) )
  274.     fprintf(stderr, "file_add_u2d: can't insert into d2u hash table\n");
  275.     return dosNameDup;
  276. }
  277.  
  278. void file_updatedir(DirMapInfo *dmp, CLIENT *nfsRPC)
  279. {
  280.     readdirargs rd;
  281.     readdirres *rds;
  282.  
  283.     /*
  284.      * first add all the filenames that are already legal dos filenames
  285.      */
  286.     bzero(&rd.cookie, sizeof(rd.cookie));
  287.     rd.dir = dmp->fh;
  288.     rd.count = NFS_MAXDATA;
  289.     do {
  290.     entry *p;
  291.  
  292.         if ( !(rds = nfsproc_readdir_2(&rd, nfsRPC))
  293.         || rds->status != NFS_OK ) {
  294.             fprintf(stderr, "file_updatedir: RPC nfs readdir call failed\n");
  295.         break;
  296.         }
  297.         for ( p = rds->readdirres_u.reply.entries ; p ; p = p->nextentry ) {
  298.         if ( !p->nextentry )
  299.         memcpy(&rd.cookie, &p->cookie, sizeof(rd.cookie));
  300.         if ( !is_dosOk(p->name) )
  301.         continue;
  302.         file_add_u2d(dmp, p->name);
  303.         }
  304.     } while ( !rds->readdirres_u.reply.eof );
  305.     /*
  306.      * next add all the filenames that are not legal dos filenames
  307.      */
  308.     bzero(&rd.cookie, sizeof(rd.cookie));
  309.     rd.dir = dmp->fh;
  310.     rd.count = NFS_MAXDATA;
  311.     do {
  312.     entry *p;
  313.  
  314.         if ( !(rds = nfsproc_readdir_2(&rd, nfsRPC))
  315.         || rds->status != NFS_OK ) {
  316.             fprintf(stderr, "file_updatedir: RPC nfs readdir call failed\n");
  317.         break;
  318.         }
  319.         for ( p = rds->readdirres_u.reply.entries ; p ; p = p->nextentry ) {
  320.         if ( !p->nextentry )
  321.         memcpy(&rd.cookie, &p->cookie, sizeof(rd.cookie));
  322.         if ( is_dosOk(p->name) )
  323.         continue;
  324.         file_add_u2d(dmp, p->name);
  325.         }
  326.     } while ( !rds->readdirres_u.reply.eof );
  327. }
  328.  
  329. DirMapInfo *file_get_mapinfo(nfs_fh *fh, CLIENT *nfsRPC)
  330. {
  331.     DirMapInfo *dmp;
  332.  
  333.     if ( !MapHT && !(MapHT = hash_new(MAP_HT_Z)) ) {
  334.     fprintf(stderr, "file_get_mapinfo: can't create dir hash table\n");
  335.     return (DirMapInfo*)NULL;
  336.     }
  337.     if ( dmp = (DirMapInfo*)hash_find(MapHT, (char*)fh, sizeof(*fh)) ) {
  338.     dmp->lastUsage = time((time_t*)NULL);
  339.         return dmp;
  340.     }
  341.     /*
  342.      * Create a new DirMapInfo structure and insert the contents
  343.      * of the unix directory.
  344.      */
  345.     if ( !(dmp = (DirMapInfo*)malloc(sizeof(DirMapInfo))) ) {
  346.     fprintf(stderr, "file_get_mapinfo: can't allocate memory\n");
  347.     return (DirMapInfo*)NULL;
  348.     }
  349.     dmp->lastUsage = time((time_t*)NULL);
  350.     dmp->fh = *fh;
  351.     if ( !(dmp->d2u = hash_new(FILE_HT_Z)) || !(dmp->u2d = hash_new(FILE_HT_Z)) ) {
  352.     fprintf(stderr, "file_get_mapinfo: can't create file map hash tables\n");
  353.     free(dmp);
  354.     return (DirMapInfo*)NULL;
  355.     }
  356.     if ( hash_insert(MapHT, (char*)&dmp->fh, sizeof(dmp->fh), dmp) ) {
  357.     fprintf(stderr, "file_get_mapinfo: can't insert into hash table\n");
  358.     exit(1);
  359.     }
  360.     file_updatedir(dmp, nfsRPC);
  361.     return dmp;
  362. }
  363.  
  364. /*
  365.  * Given a unix file name, and the directory's nfs file handle,
  366.  * return the corresponding dos file name.
  367.  */
  368. char *file_unix2dos(nfs_fh *fh, char *unixName, CLIENT *nfsRPC)
  369. {
  370. #ifdef FIX_FILENAMES
  371.     DirMapInfo *dmp = file_get_mapinfo(fh, nfsRPC);
  372.  
  373.     if ( !dmp ) return unixName;
  374.     return file_add_u2d(dmp, unixName);
  375. #else
  376.     return unixName;
  377. #endif
  378. }
  379.  
  380. /*
  381.  * Given a dos file name, and the directory's nfs file handle,
  382.  * return the corresponding unix file name.
  383.  */
  384. char *file_dos2unix(nfs_fh *fh, char *dosName, CLIENT *nfsRPC)
  385. {
  386. #ifdef FIX_FILENAMES
  387.     DirMapInfo *dmp = file_get_mapinfo(fh, nfsRPC);
  388.     static char lowerDosName[16];
  389.     int i;
  390.  
  391.     for ( i = 0 ; dosName[i] && i < 15 ; i++ )
  392.     lowerDosName[i] = tolower(dosName[i]);
  393.     lowerDosName[i] = '\0';
  394.     if ( !dmp ) return lowerDosName;
  395.     return file_add_d2u(dmp, lowerDosName);
  396. #else
  397.     return dosName;
  398. #endif
  399. }
  400.  
  401. /*
  402.  * called by the hash_loopall function in file_destroy_map_entry()
  403.  */
  404. static int file_destroy_u2d_entry(HashTable *ht, HashEntry *entry, void *arg)
  405. {
  406.     free(entry->name);
  407.     free(entry->data);
  408.     return 0;
  409. }
  410.  
  411. /*
  412.  * called by the hash_loopall function in file_map_destroy_all()
  413.  */
  414. static int file_destroy_map_entry(HashTable *ht, HashEntry *entry, void *arg)
  415. {
  416.     DirMapInfo *dmp = (DirMapInfo*)entry->data;
  417.     /*
  418.      * Free the unix and dos names in the u2d hash table.  The same pointers
  419.      * are used in the d2u hash table, so we don't need to free d2u entries.
  420.      */
  421.     hash_loopall(dmp->u2d, file_destroy_u2d_entry, NULL);
  422.     /*
  423.      * Free everything else
  424.      */
  425.     hash_free(dmp->u2d);
  426.     hash_free(dmp->d2u);
  427.     free(dmp);
  428.     hash_delete_entry(MapHT, entry);
  429.     return 0;
  430. }
  431.  
  432. /*
  433.  * Given a directory's file handle, destroy the file map info
  434.  */
  435. void file_destroy_map(nfs_fh *fh)
  436. {
  437.     HashEntry *entry;
  438.     
  439.     if ( !MapHT || !(entry = hash_find_entry(MapHT, (char*)fh, sizeof(*fh))) )
  440.         return;
  441.     file_destroy_map_entry(MapHT, entry, NULL);
  442. }
  443.  
  444. /*
  445.  * Destroy all the file map info
  446.  */
  447. void file_destroy_all_maps(void)
  448. {
  449.     if ( !MapHT )
  450.         return;
  451.     hash_loopall(MapHT, file_destroy_map_entry, NULL);
  452. }
  453.  
  454. /*
  455.  * called by the hash_loopall function in file_destroy_old_maps()
  456.  */
  457. static int file_destroy_old_map_entry(HashTable *ht, HashEntry *entry,
  458.                       time_t *oldest)
  459. {
  460.     DirMapInfo *dmp = (DirMapInfo*)entry->data;
  461.     if ( dmp->lastUsage < *oldest ) {
  462.     d_printf("file_destroy_old_map_entry: destroying old directory map\n");
  463.         return file_destroy_map_entry(ht, entry, NULL);
  464.     } else {
  465.     return 0;
  466.     }
  467. }
  468.  
  469. /*
  470.  * Destroy old file map info (anything with a lastUsage more than
  471.  * ago seconds ago).
  472.  */
  473. void file_destroy_old_maps(int age)
  474. {
  475.     time_t oldest = time((time_t*)NULL) - age;
  476.  
  477.     if ( !MapHT )
  478.         return;
  479.     d_printf("file_destroy_old_maps: checking for old directory maps\n");
  480.     hash_loopall(MapHT, file_destroy_old_map_entry, &oldest);
  481. }
  482.