Blame src/LsCache.cc

Packit Service a2489d
/*
Packit Service a2489d
 * lftp - file transfer program
Packit Service a2489d
 *
Packit Service a2489d
 * Copyright (c) 1996-2012 by Alexander V. Lukyanov (lav@yars.free.net)
Packit Service a2489d
 *
Packit Service a2489d
 * This program is free software; you can redistribute it and/or modify
Packit Service a2489d
 * it under the terms of the GNU General Public License as published by
Packit Service a2489d
 * the Free Software Foundation; either version 3 of the License, or
Packit Service a2489d
 * (at your option) any later version.
Packit Service a2489d
 *
Packit Service a2489d
 * This program is distributed in the hope that it will be useful,
Packit Service a2489d
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a2489d
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a2489d
 * GNU General Public License for more details.
Packit Service a2489d
 *
Packit Service a2489d
 * You should have received a copy of the GNU General Public License
Packit Service a2489d
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit Service a2489d
 */
Packit Service a2489d
Packit Service a2489d
#include <config.h>
Packit Service a2489d
#include <assert.h>
Packit Service a2489d
#include "FileAccess.h"
Packit Service a2489d
#include "LsCache.h"
Packit Service a2489d
#include "plural.h"
Packit Service a2489d
#include "misc.h"
Packit Service a2489d
Packit Service a2489d
int LsCacheEntry::EstimateSize() const
Packit Service a2489d
{
Packit Service a2489d
   int size=sizeof(*this);
Packit Service a2489d
   size+=LsCacheEntryLoc::EstimateSize();
Packit Service a2489d
   size+=LsCacheEntryData::EstimateSize();
Packit Service a2489d
   return size;
Packit Service a2489d
}
Packit Service a2489d
LsCacheEntryLoc::LsCacheEntryLoc(const FileAccess *p_loc,const char *a,int m)
Packit Service a2489d
   : arg(a), mode(m)
Packit Service a2489d
{
Packit Service a2489d
   loc=p_loc->Clone();
Packit Service a2489d
   loc->Suspend();
Packit Service a2489d
}
Packit Service a2489d
const char *LsCacheEntryLoc::GetClosure() const
Packit Service a2489d
{
Packit Service a2489d
   return loc->GetHostName();
Packit Service a2489d
}
Packit Service a2489d
LsCacheEntryData::LsCacheEntryData(int e,const char *d,int l,const FileSet *fs)
Packit Service a2489d
{
Packit Service a2489d
   SetData(e,d,l,fs);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
LsCacheEntry::LsCacheEntry(const FileAccess *p_loc,const char *a,int m,int e,const char *d,int l,const FileSet *fs)
Packit Service a2489d
   : LsCacheEntryLoc(p_loc,a,m), LsCacheEntryData(e,d,l,fs)
Packit Service a2489d
{
Packit Service a2489d
   SetResource(e==FA::OK?"cache:expire":"cache:expire-negative",GetClosure());
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void LsCacheEntryData::SetData(int e,const char *d,int l,const FileSet *fs)
Packit Service a2489d
{
Packit Service a2489d
   afset=fs?new FileSet(fs):0;
Packit Service a2489d
   data.nset(d,l);
Packit Service a2489d
   err_code=e;
Packit Service a2489d
}
Packit Service a2489d
void LsCacheEntryData::GetData(int *e,const char **d,int *l,const FileSet **fs)
Packit Service a2489d
{
Packit Service a2489d
   if(d && l)
Packit Service a2489d
   {
Packit Service a2489d
      *d=data;
Packit Service a2489d
      *l=data.length();
Packit Service a2489d
   }
Packit Service a2489d
   if(fs)
Packit Service a2489d
      *fs=afset;
Packit Service a2489d
   *e=err_code;
Packit Service a2489d
}
Packit Service a2489d
bool LsCacheEntryLoc::Matches(const FileAccess *p_loc,const char *a,int m)
Packit Service a2489d
{
Packit Service a2489d
   return (m==-1 || mode==m) && arg.eq(a) && p_loc->SameLocationAs(loc);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
ResDecl res_cache_empty_listings("cache:cache-empty-listings","no",ResMgr::BoolValidate,0);
Packit Service a2489d
ResDecl res_cache_enable("cache:enable","yes",ResMgr::BoolValidate,0);
Packit Service a2489d
ResDecl res_cache_expire("cache:expire","60m",ResMgr::TimeIntervalValidate,0);
Packit Service a2489d
ResDecl res_cache_expire_neg("cache:expire-negative","1m",ResMgr::TimeIntervalValidate,0);
Packit Service a2489d
ResDecl res_cache_size  ("cache:size","16M",ResMgr::UNumberValidate,ResMgr::NoClosure);
Packit Service a2489d
Packit Service a2489d
LsCache::LsCache() : Cache(&res_cache_size,&res_cache_enable) {}
Packit Service a2489d
Packit Service a2489d
void LsCache::Add(const FileAccess *p_loc,const char *a,int m,int e,const char *d,int l,const FileSet *fs)
Packit Service a2489d
{
Packit Service a2489d
   if(!strcmp(p_loc->GetProto(),"file"))
Packit Service a2489d
      return;  // don't cache local objects
Packit Service a2489d
   if(l == 0 &&
Packit Service a2489d
	 !res_cache_empty_listings.QueryBool(p_loc->GetHostName()))
Packit Service a2489d
      return;
Packit Service a2489d
   if(e!=FA::OK && e!=FA::NO_FILE && e!=FA::NOT_SUPP)
Packit Service a2489d
      return;
Packit Service a2489d
Packit Service a2489d
   Trim();
Packit Service a2489d
Packit Service a2489d
   LsCacheEntry *c=Find(p_loc,a,m);
Packit Service a2489d
   if(!c)
Packit Service a2489d
   {
Packit Service a2489d
      if(!IsEnabled(p_loc->GetHostName()))
Packit Service a2489d
	 return;
Packit Service a2489d
      AddCacheEntry(new LsCacheEntry(p_loc,a,m,e,d,l,fs));
Packit Service a2489d
   }
Packit Service a2489d
   else
Packit Service a2489d
   {
Packit Service a2489d
      c->SetData(e,d,l,fs);
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void LsCache::Add(const FileAccess *p_loc,const char *a,int m,int e,const Buffer *ubuf,const FileSet *fs)
Packit Service a2489d
{
Packit Service a2489d
   if(!ubuf->IsSaving())
Packit Service a2489d
      return;
Packit Service a2489d
Packit Service a2489d
   const char *cache_buffer;
Packit Service a2489d
   int cache_buffer_size;
Packit Service a2489d
   if(e)
Packit Service a2489d
   {
Packit Service a2489d
      cache_buffer=ubuf->ErrorText();
Packit Service a2489d
      cache_buffer_size=strlen(cache_buffer)+1;
Packit Service a2489d
   }
Packit Service a2489d
   else
Packit Service a2489d
      ubuf->GetSaved(&cache_buffer,&cache_buffer_size);
Packit Service a2489d
   LsCache::Add(p_loc,a,m,e,cache_buffer,cache_buffer_size,fs);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
LsCacheEntry *LsCache::Find(const FileAccess *p_loc,const char *a,int m)
Packit Service a2489d
{
Packit Service a2489d
   if(!IsEnabled(p_loc->GetHostName()))
Packit Service a2489d
      return 0;
Packit Service a2489d
Packit Service a2489d
   LsCacheEntry *c;
Packit Service a2489d
   for(c=IterateFirst(); c; c=IterateNext())
Packit Service a2489d
   {
Packit Service a2489d
      if(c->Matches(p_loc,a,m))
Packit Service a2489d
	 break;
Packit Service a2489d
   }
Packit Service a2489d
   if(c && c->Stopped())
Packit Service a2489d
   {
Packit Service a2489d
      Trim();
Packit Service a2489d
      return 0;
Packit Service a2489d
   }
Packit Service a2489d
   return c;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
bool LsCache::Find(const FileAccess *p_loc,const char *a,int m,int *e,const char **d,int *l,const FileSet **fs)
Packit Service a2489d
{
Packit Service a2489d
   LsCacheEntry *c=Find(p_loc,a,m);
Packit Service a2489d
   if(!c)
Packit Service a2489d
      return false;
Packit Service a2489d
   c->GetData(e,d,l,fs);
Packit Service a2489d
   return true;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
const FileSet *LsCache::FindFileSet(const FileAccess *p_loc,const char *a,int m)
Packit Service a2489d
{
Packit Service a2489d
   LsCacheEntry *c=Find(p_loc,a,m);
Packit Service a2489d
   if(!c)
Packit Service a2489d
      return 0;
Packit Service a2489d
   return c->GetFileSet(c->loc);
Packit Service a2489d
}
Packit Service a2489d
const FileSet *LsCacheEntryData::GetFileSet(const FileAccess *parser)
Packit Service a2489d
{
Packit Service a2489d
   if(afset)
Packit Service a2489d
      return afset;
Packit Service a2489d
   if(err_code!=FA::OK)
Packit Service a2489d
      return 0;
Packit Service a2489d
   afset=parser->ParseLongList(data, data.length());
Packit Service a2489d
   return afset;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void LsCache::UpdateFileSet(const FileAccess *p_loc,const char *a,int m,const FileSet *fs)
Packit Service a2489d
{
Packit Service a2489d
   if(!fs)
Packit Service a2489d
      return;
Packit Service a2489d
   LsCacheEntry *c=Find(p_loc,a,m);
Packit Service a2489d
   if(!c)
Packit Service a2489d
      return;
Packit Service a2489d
   c->UpdateFileSet(fs);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void LsCache::List()
Packit Service a2489d
{
Packit Service a2489d
   Trim();
Packit Service a2489d
Packit Service a2489d
   long vol=0;
Packit Service a2489d
   for(LsCacheEntry *c=IterateFirst(); c; c=IterateNext())
Packit Service a2489d
      vol+=c->EstimateSize();
Packit Service a2489d
Packit Service a2489d
   printf(plural("%ld $#l#byte|bytes$ cached",vol),vol);
Packit Service a2489d
Packit Service a2489d
   long sizelimit=res_cache_size.Query(0);
Packit Service a2489d
   if(sizelimit<0)
Packit Service a2489d
      puts(_(", no size limit"));
Packit Service a2489d
   else
Packit Service a2489d
      printf(_(", maximum size %ld\n"),sizelimit);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void LsCache::Changed(change_mode m,const FileAccess *f,const char *dir)
Packit Service a2489d
{
Packit Service a2489d
   xstring fdir(dir_file(f->GetCwd(),dir));
Packit Service a2489d
   if(m==FILE_CHANGED)
Packit Service a2489d
      dirname_modify(fdir);
Packit Service a2489d
Packit Service a2489d
   LsCacheEntry *c=IterateFirst();
Packit Service a2489d
   while(c)
Packit Service a2489d
   {
Packit Service a2489d
      const FileAccess *sloc=c->loc;
Packit Service a2489d
      if(f->SameLocationAs(sloc) || (f->SameSiteAs(sloc)
Packit Service a2489d
	       && (m==TREE_CHANGED?
Packit Service a2489d
		     !strncmp(fdir,dir_file(sloc->GetCwd(),c->arg),fdir.length())
Packit Service a2489d
		   : !strcmp (fdir,dir_file(sloc->GetCwd(),c->arg)))))
Packit Service a2489d
	 c=IterateDelete();
Packit Service a2489d
      else
Packit Service a2489d
	 c=IterateNext();
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
/* Mark a path as a directory or file. (We have other ways of knowing this;
Packit Service a2489d
 * this is the most explicit and least expensive.) */
Packit Service a2489d
void LsCache::SetDirectory(const FileAccess *p_loc, const char *path, bool dir)
Packit Service a2489d
{
Packit Service a2489d
   if(!path)
Packit Service a2489d
      return;
Packit Service a2489d
Packit Service a2489d
   FileAccess::Path new_cwd = p_loc->GetCwd();
Packit Service a2489d
   new_cwd.Change(path,!dir);
Packit Service a2489d
   SMTaskRef<FileAccess> new_p_loc(p_loc->Clone());
Packit Service a2489d
   new_p_loc->SetCwd(new_cwd);
Packit Service a2489d
Packit Service a2489d
   const char *entry = dir? "1":"0";
Packit Service a2489d
   LsCache::Add(new_p_loc,"",FileAccess::CHANGE_DIR, dir?FA::OK:FA::NO_FILE, entry, strlen(entry));
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
/* This is a hint function. If file type is really needed, use GetFileInfo
Packit Service a2489d
 * with showdir set to true. (GetFileInfo uses this function.)
Packit Service a2489d
 * Returns -1 if type is not known, 1 if a directory, 0 if a file. */
Packit Service a2489d
Packit Service a2489d
int LsCache::IsDirectory(const FileAccess *p_loc,const char *dir_c)
Packit Service a2489d
{
Packit Service a2489d
   FileAccess::Path new_cwd(p_loc->GetCwd());
Packit Service a2489d
   new_cwd.Change(dir_c);
Packit Service a2489d
   FileAccessRef new_p_loc(p_loc->Clone());
Packit Service a2489d
   new_p_loc->SetCwd(new_cwd);
Packit Service a2489d
Packit Service a2489d
   int ret = -1;
Packit Service a2489d
Packit Service a2489d
   /* Cheap tests first:
Packit Service a2489d
    *
Packit Service a2489d
    * First, we know the path is a directory or not if we have an expicit
Packit Service a2489d
    * CHANGE_DIR entry for it. */
Packit Service a2489d
   const char *buf_c;
Packit Service a2489d
   int bufsiz;
Packit Service a2489d
   int e;
Packit Service a2489d
   if(Find(new_p_loc, "", FileAccess::CHANGE_DIR, &e, &buf_c,&bufsiz))
Packit Service a2489d
   {
Packit Service a2489d
      assert(bufsiz==1);
Packit Service a2489d
      return (e==FA::OK);
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   /* We know the path is a directory if we have a cache entry for it.  This is
Packit Service a2489d
    * true regardless of the list type.  (Unless it's a CHANGE_DIR entry; do this
Packit Service a2489d
    * test after the CHANGE_DIR check.) */
Packit Service a2489d
   if(Find(new_p_loc, "", FA::LONG_LIST, &e, 0,0))
Packit Service a2489d
      return(e==FA::OK);
Packit Service a2489d
   if(Find(new_p_loc, "", FA::MP_LIST, &e, 0,0))
Packit Service a2489d
      return(e==FA::OK);
Packit Service a2489d
   if(Find(new_p_loc, "", FA::LIST, &e, 0,0))
Packit Service a2489d
      return(e==FA::OK);
Packit Service a2489d
Packit Service a2489d
   /* We know this is a file or a directory if the dirname is cached and
Packit Service a2489d
    * contains the basename. */
Packit Service a2489d
   {
Packit Service a2489d
      const char *bn=basename_ptr(new_cwd.path);
Packit Service a2489d
      bn=alloca_strdup(bn); // save basename
Packit Service a2489d
Packit Service a2489d
      new_cwd.Change("..");
Packit Service a2489d
      new_p_loc->SetCwd(new_cwd);
Packit Service a2489d
Packit Service a2489d
      const FileSet *fs=FindFileSet(new_p_loc, "", FA::MP_LIST);
Packit Service a2489d
      if(!fs)
Packit Service a2489d
	 fs=FindFileSet(new_p_loc, "", FA::LONG_LIST);
Packit Service a2489d
      if(fs)
Packit Service a2489d
      {
Packit Service a2489d
	 FileInfo *fi=fs->FindByName(bn);
Packit Service a2489d
	 if(fi && (fi->defined&fi->TYPE))
Packit Service a2489d
	    return(fi->filetype == fi->DIRECTORY);
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   return ret;
Packit Service a2489d
}