Sync
authorAndrew McNab <andrew.mcnab@manchester.ac.uk>
Wed, 26 Sep 2007 15:48:28 +0000 (15:48 +0000)
committerAndrew McNab <andrew.mcnab@manchester.ac.uk>
Wed, 26 Sep 2007 15:48:28 +0000 (15:48 +0000)
org.gridsite.core/src/grst_admin_file.c
org.gridsite.core/src/grst_admin_main.c
org.gridsite.core/src/mod_gridsite.c
org.gridsite.core/src/slashgrid.c

index d6619a6..fbc00a2 100644 (file)
@@ -264,7 +264,8 @@ void deletefileaction(char *dn, GRSTgaclPerm perm, char *help_uri,
   struct dirent *subdirfile_ent;
   DIR           *subDIR;
 
-  if (((strcmp(file, GRST_ACL_FILE) != 0) && !GRSTgaclPermHasWrite(perm)) ||
+  if ((file[0] == '\0') ||
+      ((strcmp(file, GRST_ACL_FILE) != 0) && !GRSTgaclPermHasWrite(perm)) ||
       ((strcmp(file, GRST_ACL_FILE) == 0) && !GRSTgaclPermHasAdmin(perm)))
                                                GRSThttpError("403 Forbidden");
 
@@ -438,8 +439,9 @@ void editfileaction(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
   FILE         *fp;
   GRSThttpBody  bp;
   
-  if (!GRSTgaclPermHasWrite(perm) || (strcmp(file, GRST_ACL_FILE) == 0))
-                                               GRSThttpError("403 Forbidden");
+  if ((file[0] == '\0') ||
+      !GRSTgaclPermHasWrite(perm) || 
+      (strcmp(file, GRST_ACL_FILE) == 0)) GRSThttpError("403 Forbidden");
                                                  
   dnlistsuri = getenv("GRST_DN_LISTS_URI");
   if (dnlistsuri == NULL) dnlistsuri = getenv("REDIRECT_GRST_DN_LISTS_URI");
@@ -576,7 +578,7 @@ void renameaction(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
   if (!GRSTgaclPermHasWrite(perm) || (strcmp(file, GRST_ACL_FILE) == 0)) 
                                               GRSThttpError("403 Forbidden");
                                               
-  if (index(file, '/') != NULL) GRSThttpError("403 Forbidden");
+  if (file[0] == '\0') GRSThttpError("403 Forbidden");
 
   dir_path_file = malloc(strlen(dir_path) + strlen(file) + 2);  
   strcpy(dir_path_file, dir_path);
@@ -586,8 +588,14 @@ void renameaction(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
   if (stat(dir_path_file, &statbuf) != 0) GRSThttpError("404 Not Found");
 
   newfile = GRSThttpGetCGI("newfile");
-
-  if ((strcmp(newfile, GRST_ACL_FILE) == 0) ||
+  if ((index(newfile, '/') != NULL) ||
+      (index(newfile, '<') != NULL) ||
+      (index(newfile, '>') != NULL) ||
+      (index(newfile, '&') != NULL) ||
+      (index(newfile, '"') != NULL)) newfile[0] = '\0';
+
+  if ((newfile[0] == '\0') ||
+      (strcmp(newfile, GRST_ACL_FILE) == 0) ||
       (strcmp(newfile, file) == 0)) GRSThttpError("403 Forbidden");
 
   dir_path_newfile = malloc(strlen(dir_path) + strlen(newfile) + 2);  
@@ -843,7 +851,7 @@ void printfile(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
   
   if (!GRSTgaclPermHasRead(perm)) GRSThttpError("403 Forbidden");
 
-  if (index(file, '/') != NULL) GRSThttpError("403 Forbidden");
+  if (file[0] == '\0') GRSThttpError("403 Forbidden");
   
   dir_path_file = malloc(strlen(dir_path) + strlen(file) + 2);
   
@@ -881,7 +889,7 @@ void filehistory(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
   
   if (!GRSTgaclPermHasRead(perm)) GRSThttpError("403 Forbidden");
 
-  if (index(file, '/') != NULL) GRSThttpError("403 Forbidden");
+  if (file[0] == '\0') GRSThttpError("403 Forbidden");
   
   puts("Status: 200 OK\nContent-Type: text/html");
                                                                                 
@@ -989,7 +997,7 @@ void ziplist(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
     
   if (!GRSTgaclPermHasRead(perm)) GRSThttpError("403 Forbidden");
 
-  if (index(file, '/') != NULL) GRSThttpError("403 Forbidden");
+  if (file[0] == '\0') GRSThttpError("403 Forbidden");
   
   puts("Status: 200 OK\nContent-Type: text/html");
                                                                                 
@@ -1043,7 +1051,7 @@ void unzipfile(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
     
   if (!GRSTgaclPermHasWrite(perm)) GRSThttpError("403 Forbidden");
   
-  if (index(file, '/') != NULL) GRSThttpError("403 Forbidden");
+  if (file[0] == '\0') GRSThttpError("403 Forbidden");
   
   puts("Status: 200 OK\nContent-Type: text/html");
                                                                                 
@@ -1094,7 +1102,7 @@ void editfileform(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
   
   if (!GRSTgaclPermHasWrite(perm)) GRSThttpError("403 Forbidden");
   
-  if (index(file, '/') != NULL) GRSThttpError("403 Forbidden");
+  if (file[0] == '\0') GRSThttpError("403 Forbidden");
   
   dir_path_file = malloc(strlen(dir_path) + strlen(file) + 2);
   
@@ -1185,7 +1193,8 @@ void editdnlistform(char *dn, GRSTgaclPerm perm, char *help_uri, char *dir_path,
   dnlistsuri = getenv("GRST_DN_LISTS_URI");
   if (dnlistsuri == NULL) dnlistsuri = getenv("REDIRECT_GRST_DN_LISTS_URI");
 
-  if (!GRSTgaclPermHasWrite(perm) ||
+  if ((file[0] == '\0') ||
+      !GRSTgaclPermHasWrite(perm) ||
       (dnlistsuri == NULL) ||
       (strncmp(dnlistsuri, dir_uri, strlen(dnlistsuri)) != 0)) 
                                              GRSThttpError("403 Forbidden");
index 069a2f4..45e819b 100644 (file)
@@ -294,9 +294,16 @@ int main()
     }
   
   cmd    = GRSThttpGetCGI("cmd");
-  file   = GRSThttpGetCGI("file");
   button = GRSThttpGetCGI("button");
 
+  file   = GRSThttpGetCGI("file");
+  
+  if ((index(file, '/') != NULL) ||
+      (index(file, '<') != NULL) ||
+      (index(file, '>') != NULL) ||
+      (index(file, '&') != NULL) ||
+      (index(file, '"') != NULL)) file[0] = '\0';
+
   /* file and directory functions in grst_admin_file.c */
 
   if (strcmp(cmd, "header") == 0) 
index f5c3458..8ddd0be 100644 (file)
@@ -210,6 +210,51 @@ int parse_content_range(request_rec *r, apr_off_t *range_start,
     return 1;
 }
 
+char *html_escape(apr_pool_t *pool, char *s)
+{
+    int    htmlspecials, i;
+    char  *escaped, *p;
+
+    for (htmlspecials=0,p=s; *p != '\0'; ++p) 
+      if ((*p == '<') || (*p == '>') || (*p == '&') || (*p == '"')) 
+          ++htmlspecials;
+
+    escaped = apr_palloc(pool, strlen(s) + htmlspecials * 6);
+        
+    for (i=0,p=s; *p != '\0'; ++p)
+       {
+             if      (*p == '<') 
+                { 
+                  strcpy(&escaped[i], "&lt;");
+                  i += 4;
+                }
+            else if (*p == '>') 
+                {
+                  strcpy(&escaped[i], "&gt;");
+                  i += 4;
+                }
+            else if (*p == '&') 
+                {
+                  strcpy(&escaped[i], "&amp;");
+                  i += 5;
+                }
+            else if (*p == '"') 
+                {
+                  strcpy(&escaped[i], "&quot;");
+                  i += 6;
+                }
+            else 
+                {
+                  escaped[i] = *p;
+                  ++i;
+                }                  
+       }
+
+    escaped[i] = '\0';
+   
+    return escaped;
+}
+
 char *make_admin_footer(request_rec *r, mod_gridsite_dir_cfg *conf,
                         int isdirectory)
 /*
@@ -273,12 +318,13 @@ char *make_admin_footer(request_rec *r, mod_gridsite_dir_cfg *conf,
         (strncmp(grst_cred_auri_0, "dn:", 3) == 0))
       {
          dn = &grst_cred_auri_0[3];
-         if (dn[0] == '\0') dn = NULL;
+         if (dn[0] == '\0') dn = NULL;         
       }
   
     if (dn != NULL) 
       {
-        temp = apr_psprintf(r->pool, "You are %s<br>\n", dn);
+        temp = apr_psprintf(r->pool, 
+                            "You are %s<br>\n", html_escape(r->pool,dn));
         out = apr_pstrcat(r->pool, out, temp, NULL);
                
         if (r->notes != NULL)
@@ -355,8 +401,8 @@ char *make_admin_footer(request_rec *r, mod_gridsite_dir_cfg *conf,
     return out;
 }
 
-void delegation_header(request_rec *r, mod_gridsite_dir_cfg *conf){
-
+void delegation_header(request_rec *r, mod_gridsite_dir_cfg *conf)
+{
   apr_table_add(r->headers_out,
                 apr_pstrdup(r->pool, "Proxy-Delegation-Service"),
                 apr_psprintf(r->pool,"https://%s%s", r->hostname, conf->delegationuri));
@@ -535,7 +581,8 @@ int html_dir_list(request_rec *r, mod_gridsite_dir_cfg *conf)
     int    i, fd, n, nn;
     char  *buf, *p, *s, *head_formatted, *header_formatted,
           *body_formatted, *admin_formatted, *footer_formatted, *temp,
-           modified[99], *d_namepath, *indexheaderpath, *indexheadertext;
+           modified[99], *d_namepath, *indexheaderpath, *indexheadertext,
+           *encoded, *escaped;
     size_t length;
     struct stat statbuf;
     struct tm   mtime_tm;
@@ -630,24 +677,30 @@ int html_dir_list(request_rec *r, mod_gridsite_dir_cfg *conf)
                strftime(modified, sizeof(modified), 
               "<td align=right>%R</td><td align=right>%e&nbsp;%b&nbsp;%y</td>",
                         &mtime_tm);    
-                              
+
+               encoded = GRSThttpUrlMildencode(namelist[n]->d_name);
+               escaped = html_escape(r->pool, namelist[n]->d_name);
+
                if (S_ISDIR(statbuf.st_mode))
                     temp = apr_psprintf(r->pool, 
                       "<tr><td><a href=\"%s/\" content-length=\"%ld\" "
                       "last-modified=\"%ld\">"
                       "%s/</a></td>"
                       "<td align=right>%ld</td>%s</tr>\n", 
-                      namelist[n]->d_name, statbuf.st_size, statbuf.st_mtime,
-                      namelist[n]->d_name
+                      encoded, statbuf.st_size, statbuf.st_mtime,
+                      escaped
                       statbuf.st_size, modified);
                else temp = apr_psprintf(r->pool, 
                       "<tr><td><a href=\"%s\" content-length=\"%ld\" "
                       "last-modified=\"%ld\">"
                       "%s</a></td>"
                       "<td align=right>%ld</td>%s</tr>\n", 
-                      namelist[n]->d_name, statbuf.st_size, statbuf.st_mtime,
-                      namelist[n]->d_name
+                      encoded, statbuf.st_size, statbuf.st_mtime,
+                      escaped
                       statbuf.st_size, modified);
+                      
+               free(encoded);
+               /* escaped done with pool so no free() */
 
                body_formatted = apr_pstrcat(r->pool,body_formatted,temp,NULL);
              }
@@ -1120,7 +1173,8 @@ static void recurse4dirlist(char *dirname, time_t *dirs_time,
 /* try to find DN Lists in dir[] and its subdirs that match the fulluri[]
    prefix. add blobs of HTML to body as they are found. */
 {
-   char          *unencname, modified[99], *oneline, *d_namepath;
+   char          *unencname, modified[99], *oneline, *d_namepath,
+                 *mildencoded;
    DIR           *oneDIR;
    struct dirent *onedirent;
    struct tm      mtime_tm;
@@ -1159,17 +1213,22 @@ static void recurse4dirlist(char *dirname, time_t *dirs_time,
                   strftime(modified, sizeof(modified), 
               "<td align=right>%R</td><td align=right>%e&nbsp;%b&nbsp;%y</td>",
                        &mtime_tm);
-
+                  
+                  mildencoded = GRSThttpUrlMildencode(&unencname[fullurilen]);
+                 
                   oneline = apr_psprintf(pool,
                                      "<tr><td><a href=\"%s\" "
                                      "content-length=\"%ld\" "
                                      "last-modified=\"%ld\">"
                                      "%s</a></td>"
                                      "<td align=right>%ld</td>%s</tr>\n", 
-                                     &unencname[fullurilen], statbuf.st_size, 
-                                     statbuf.st_mtime, unencname, 
+                                     mildencoded, statbuf.st_size, 
+                                     statbuf.st_mtime, 
+                                     html_escape(pool, unencname), 
                                      statbuf.st_size, modified);
 
+                  free(mildencoded);
+
                   *body = apr_pstrcat(pool, *body, oneline, NULL);
                 }      
                       
index cefb5a4..33d2413 100644 (file)
@@ -140,6 +140,8 @@ struct grst_handle { pthread_mutex_t        mutex;
                      char              *capath;
                      time_t            last_used;
                    }  handles[GRST_SLASH_MAX_HANDLES];
+
+pthread_mutex_t cache_mutex;
  
 int debugmode         = 0;
 int number_of_tries   = 1, sitecast_domain_len = 0;
@@ -175,17 +177,28 @@ int cleanup_recurse(char *currentdirname)
           
       if (asprintf(&s, "%s/%s", currentdirname, ent->d_name) == -1) continue;
     
-      syslog(LOG_DEBUG, "cleanup_thread() stat(%s)", s);
+      if (debugmode) syslog(LOG_DEBUG, "cleanup_thread() stat(%s)", s);
 
+      pthread_mutex_lock(&cache_mutex);
       if (stat(s, &ent_stat) == 0) /* ignore if gone already! */
         {
           if (S_ISDIR(ent_stat.st_mode))
             {
-              if ((cleanup_recurse(s) == 0) &&
-                  (ent_stat.st_mtime < now - GRST_SLASH_CACHE_EXPIRE))
+              pthread_mutex_unlock(&cache_mutex); /* unlock during recurse */
+            
+              if (cleanup_recurse(s) == 0) /* directory is now empty */
                 {
-                  syslog(LOG_DEBUG, "cleanup_thread() rmdir(%s)", s);
-                  rmdir(s);
+                  pthread_mutex_lock(&cache_mutex); /* lock for stat/rmdir */
+                
+                  if ((stat(s, &ent_stat) == 0)   &&
+                      (S_ISDIR(ent_stat.st_mode)) &&
+                      (ent_stat.st_mtime < now - GRST_SLASH_CACHE_EXPIRE))
+                       rmdir(s);
+                  else ++num_left_here;
+                  
+                  pthread_mutex_unlock(&cache_mutex);
+
+                  if (debugmode) syslog(LOG_DEBUG, "cleanup_thread() rmdir(%s)", s);
                 }
               else ++num_left_here;
             }
@@ -193,12 +206,15 @@ int cleanup_recurse(char *currentdirname)
             {
               if (ent_stat.st_mtime < now - GRST_SLASH_CACHE_EXPIRE)
                 {
-                  syslog(LOG_DEBUG, "cleanup_thread() unlink(%s)", s);
+                  if (debugmode) syslog(LOG_DEBUG, "cleanup_thread() unlink(%s)", s);
                   unlink(s);
                 }
               else ++num_left_here;
+              
+              pthread_mutex_unlock(&cache_mutex);
             }
         }
+      else pthread_mutex_unlock(&cache_mutex);
 
       free(s);
     }
@@ -212,14 +228,10 @@ void *cleanup_thread(void *unused)
 {
   while (1)
    {
-     syslog(LOG_DEBUG, "cleanup_thread() scan starts");
-
      cleanup_recurse(GRST_SLASH_HEADERS);
      cleanup_recurse(GRST_SLASH_BLOCKS);
      cleanup_recurse(GRST_SLASH_TMP);
  
-     syslog(LOG_DEBUG, "cleanup_thread() scan completes");
-
      sleep(GRST_SLASH_CACHE_EXPIRE / 2);
    }   
 }
@@ -257,17 +269,20 @@ size_t headers_callback(void *ptr, size_t size, size_t nmemb, void *p)
 
         if (strptime(&s[15], "%a, %d %b %Y %T GMT", &modified_tm) != NULL)
           {
-            request_data->modified = mktime(&modified_tm);
+            modified_tm.tm_isdst = 0;
+            request_data->modified = timegm(&modified_tm);
             request_data->modified_set = 1;
           }
         else if (strptime(&s[15], "%a, %d-%b-%y %T GMT", &modified_tm) != NULL)
           {
-            request_data->modified = mktime(&modified_tm);
+            modified_tm.tm_isdst = 0;
+            request_data->modified = timegm(&modified_tm);
             request_data->modified_set = 1;
           }
         else if (strptime(&s[15], "%a %b %d %T %Y", &modified_tm) != NULL)
           {
-            request_data->modified = mktime(&modified_tm);
+            modified_tm.tm_isdst = 0;
+            request_data->modified = timegm(&modified_tm);
             request_data->modified_set = 1;
           }
       }
@@ -300,7 +315,7 @@ int debug_callback(CURL *handle, curl_infotype infotype,
      
   mesg[n] = '\0';
 
-  syslog(LOG_DEBUG, "%d %s%s%s%s", 
+  if (debugmode) syslog(LOG_DEBUG, "%d %s%s%s%s", 
                     *((int *) i), 
                     (infotype == CURLINFO_HEADER_IN ) ? "<<" : "",
                     (infotype == CURLINFO_HEADER_OUT) ? ">>" : "",
@@ -381,8 +396,7 @@ int translate_sitecast_url(char **sitecast_url, char *raw_url)
 
   for (i=0; i <= igroup; ++i)
      {
-       if (debugmode)
-        syslog(LOG_DEBUG, "Querying multicast group %d.%d.%d.%d:%d:%d:%d\n",
+       if (debugmode) syslog(LOG_DEBUG, "Querying multicast group %d.%d.%d.%d:%d:%d:%d\n",
                 groups[i].quad1, groups[i].quad2,
                 groups[i].quad3, groups[i].quad4,
                 groups[i].port, groups[i].ttl,
@@ -1223,7 +1237,7 @@ int read_headers_from_cache(struct fuse_context *fuse_ctx, char *filename,
                             off_t *length, time_t *modified)
 {
   char *encoded_filename, *disk_filename;
-  int   len;
+  int   len, fd;
   long  content_length, last_modified;
   FILE *fp;
   struct stat statbuf;
@@ -1236,15 +1250,22 @@ int read_headers_from_cache(struct fuse_context *fuse_ctx, char *filename,
   if (encoded_filename[len - 1] == '/') /* a directory */
        asprintf(&disk_filename, "%s/%d%s%s", 
                 GRST_SLASH_HEADERS, fuse_ctx->uid, encoded_filename, GRST_SLASH_DIRFILE);
-  else asprintf(&disk_filename, "%s/%d%s%s", 
+  else asprintf(&disk_filename, "%s/%d%s", 
                 GRST_SLASH_HEADERS, fuse_ctx->uid, encoded_filename);
 
   free(encoded_filename);
 
-// Change to fstat for the benefit of multiple threads:
+  if ((fd = open(disk_filename, O_RDONLY)) == -1)
+    {
+      if (debugmode) syslog(LOG_DEBUG, "open(%s) in cache fails", disk_filename);
+      free(disk_filename);
+      return 0;
+    }
 
-  if (stat(disk_filename, &statbuf) != 0) /* no cache file to read */
+  if (fstat(fd, &statbuf) != 0) /* no cache file to read */
     {
+      close(fd);
+      if (debugmode) syslog(LOG_DEBUG, "fstat(%s) in cache fails", disk_filename);
       free(disk_filename);
       return 0;
     }
@@ -1253,6 +1274,8 @@ int read_headers_from_cache(struct fuse_context *fuse_ctx, char *filename,
 
   if (statbuf.st_mtime < now - GRST_SLASH_CACHE_EXPIRE)
     {
+      close(fd);
+      if (debugmode) syslog(LOG_DEBUG, "%s in cache has expired", disk_filename);
       unlink(disk_filename); /* tidy up expired cache file */
       free(disk_filename);
       return 0;
@@ -1263,10 +1286,9 @@ int read_headers_from_cache(struct fuse_context *fuse_ctx, char *filename,
 
   if (debugmode) syslog(LOG_DEBUG, "Opening %s from cache", disk_filename);
 
-  fp = fopen(disk_filename, "r");
   free(disk_filename);
 
-  if (fp != NULL)
+  if ((fp = fdopen(fd, "r")) != NULL)
     {
       fscanf(fp, "content-length=%ld last-modified=%ld ", 
                  &content_length, &last_modified);
@@ -1281,13 +1303,15 @@ int read_headers_from_cache(struct fuse_context *fuse_ctx, char *filename,
       return 1;
     }
 
+  close(fd);
+
   return 0;
 }
 
 int write_headers_to_cache(struct fuse_context *fuse_ctx, char *filename, 
                            off_t length, time_t modified)
 {
-  int         fd, len;
+  int         fd, len, ret;
   char       *tempfile, *headline, *encoded_filename, *p, *newdir,
              *new_filename;
   struct stat statbuf;
@@ -1330,8 +1354,10 @@ int write_headers_to_cache(struct fuse_context *fuse_ctx, char *filename,
          {
            if (!S_ISDIR(statbuf.st_mode)) /* exists already - not a directory! */
              {
+               pthread_mutex_lock(&cache_mutex);
                unlink(newdir);
                mkdir(newdir, S_IRUSR | S_IWUSR | S_IXUSR);
+               pthread_mutex_unlock(&cache_mutex);
              }
            /* else it already exists as a directory - so ok */
          }
@@ -1356,10 +1382,12 @@ int write_headers_to_cache(struct fuse_context *fuse_ctx, char *filename,
       rmdir(new_filename);
     }
 
-  rename(tempfile, new_filename);
+  pthread_mutex_lock(&cache_mutex);
+  ret = rename(tempfile, new_filename);
+  pthread_mutex_unlock(&cache_mutex);
 
-  if (debugmode) syslog(LOG_DEBUG, "Added %s to cache (%ld %ld)\n", 
-                                   new_filename, length, modified);
+  if (debugmode) syslog(LOG_DEBUG, "Move %s to %s in cache (%d;%ld,%ld)\n", 
+                              tempfile, new_filename, ret, length, modified);
 
   free(tempfile);
   free(new_filename);
@@ -1585,6 +1613,8 @@ static int slashgrid_getattr(const char *rawpath, struct stat *stbuf)
   memset(stbuf, 0, sizeof(struct stat));
   stbuf->st_mode  = S_IFREG | 0755;
   stbuf->st_nlink = 1;
+  stbuf->st_uid     = fuse_ctx.uid;
+  stbuf->st_gid     = fuse_ctx.gid;
   
   if ((strcmp(rawpath, "/")      == 0) ||
       (strcmp(rawpath, "/http")  == 0) ||
@@ -1654,8 +1684,6 @@ static int slashgrid_getattr(const char *rawpath, struct stat *stbuf)
       else if (ret == 0)
         {
           stbuf->st_nlink   = 1;
-          stbuf->st_uid     = fuse_ctx.uid;
-          stbuf->st_gid     = fuse_ctx.gid;
           stbuf->st_size    = stat_tmp.st_size;
           stbuf->st_blksize = stat_tmp.st_blksize;
           stbuf->st_blocks  = stat_tmp.st_blocks;
@@ -1712,7 +1740,9 @@ static int slashgrid_getattr(const char *rawpath, struct stat *stbuf)
       free(path);
       return 0;    
     }
-  
+  else if (debugmode) syslog(LOG_DEBUG, 
+                      "Headers for %s not found in cache at %s\n", url, path);
+                        
   if (debugmode) syslog(LOG_DEBUG, "Get details for %s over network\n", url);
 
   bzero(&request_data, sizeof(struct grst_request));
@@ -1907,7 +1937,9 @@ int write_block_to_cache(struct fuse_context *fuse_ctx, char *filename,
 
   free(encoded_filename);
   
+  pthread_mutex_lock(&cache_mutex);
   rename(tempfile, new_filename);
+  pthread_mutex_unlock(&cache_mutex);
 
   if (debugmode) syslog(LOG_DEBUG, "Added %s to block cache", new_filename);
 
@@ -1917,22 +1949,41 @@ int write_block_to_cache(struct fuse_context *fuse_ctx, char *filename,
   return 0;
 }
 
-int drop_cache_blocks(struct fuse_context *fuse_ctx, char *filename)
-/* drop ALL the blocks cached for this file, and delete the directory in
-   the blocks cache for this file */
+void drop_cache_blocks(struct fuse_context *fuse_ctx, char *filename)
+/*
+   Drop ALL the blocks cached for this file by moving the whole directory
+   to GRST_SLASH_TMP and letting the cleanup thread deal with them when
+   it has time; and then remove the headers cached for this file.
+*/
 {
-  int   ret;
-  char *encoded_filename, *dirname, *blockname;
-  DIR *blocksDIR;
-  struct dirent *blocks_ent;
+  char *encoded_filename, *dirname, *headersname;
+//  DIR *blocksDIR;
+//  struct dirent *blocks_ent;
 
   encoded_filename = GRSThttpUrlMildencode(filename);
   
-  asprintf(&dirname, "%s/%d%s", 
+  /* move blocks directory */
+
+  asprintf(&dirname, "%s/%d%s",
                      GRST_SLASH_BLOCKS, fuse_ctx->uid, encoded_filename);
 
-  free(encoded_filename);
+  rename(dirname, GRST_SLASH_TMP);
+  free(dirname);
+
+  /* remove headers file */
+
+  asprintf(&headersname, "%s/%d%s",
+                     GRST_SLASH_HEADERS, fuse_ctx->uid, encoded_filename);
+
+  unlink(headersname);
+  free(headersname);
 
+  /* finish */
+
+  free(encoded_filename);
+  return;
+  
+#if 0
   blocksDIR = opendir(dirname);
   
   if (blocksDIR == NULL) /* no directory to delete (probably) */
@@ -1954,6 +2005,7 @@ int drop_cache_blocks(struct fuse_context *fuse_ctx, char *filename)
   free(dirname);  
 
   return ret ? 1 : 0; /* return 1 on error, 0 on rmdir() success */
+#endif
 }
 
 static int slashgrid_read(const char *path, char *buf, 
@@ -2027,19 +2079,24 @@ static int slashgrid_read(const char *path, char *buf,
 
        if (debugmode) syslog(LOG_DEBUG, "disk_filename=%s", disk_filename);
                  
-       if ((stat(disk_filename, &statbuf) != 0) ||
+       fd = open(disk_filename, O_RDONLY);
+       
+       if ((fd == -1) ||
+           (fstat(fd, &statbuf) != 0) ||
            (statbuf.st_mtime < now - GRST_SLASH_CACHE_EXPIRE))
          {
+           if (fd != -1) close(fd);
+         
            write_block_to_cache(&fuse_ctx, (char *) path, 
                             block_i, block_i + blocksize - 1);
+                            
+           fd = open(disk_filename, O_RDONLY);                            
          }
 
-// need to worry about cached copy being deleted (invalidated by a writing
-// thread?) between write_block_to_cache() and these reads?
-// maybe return fd from write_block_to_cache() itself???
-// the initial stat() needs to be part of this too
+       /* even if another thread deletes disk_filename between 
+          open and read, we've still got it open so can carry on */
 
-       if ((fd = open(disk_filename, O_RDONLY)) != -1)
+       if (fd != -1)
          {
            if (block_i == block_start)              
              {
@@ -2660,6 +2717,8 @@ int main(int argc, char *argv[])
        handles[i].last_used   = 0;
      }
 
+  pthread_mutex_init(&cache_mutex, NULL);
+
 //  GRSTerrorLogFunc = slashgrid_logfunc;
  
   GRSTgaclInit();