summaryrefslogtreecommitdiff
path: root/methods
diff options
context:
space:
mode:
authorDavid Kalnischkies <david@kalnischkies.de>2022-05-09 11:42:48 +0200
committerDavid Kalnischkies <david@kalnischkies.de>2024-11-22 12:40:10 +0000
commit7adf8c1fa8d519b8b57292763eb7703e7d3cc580 (patch)
tree368c3cb410c9489f193562f06f656f753af29c2d /methods
parent15124923b4cdb19af6bf642f396dab7c3e6bd074 (diff)
Look at non by-hash paths in copy and file methods
Ideally copy and file mirrors would support by-hash as well, but its harder to setup and maintain especially if you want to cache an online mirror who has by-hash enabled. We can avoid unneeded roundtrips (in the best case) and entire cache misses (in the usual worst case) by "just" telling methods that the URI we passed it has the requested file perhaps also in other paths. This is done in pseudo-relative paths as we would otherwise need to teach redirection code to rewrite those URIs as well. A method like http can easily ignore this value and await explicit instructions to look at that file, but inspecting the path in local sources via file or copy is (comparatively) free, so we just do it immediately. If that ends up being the wrong version of the file as by-hash would have protected us from we are in this feature branch now falling back to other mirrors which are like the ones online and in support of by-hash.
Diffstat (limited to 'methods')
-rw-r--r--methods/aptmethod.h11
-rw-r--r--methods/copy.cc90
-rw-r--r--methods/file.cc111
3 files changed, 126 insertions, 86 deletions
diff --git a/methods/aptmethod.h b/methods/aptmethod.h
index 1c24f3a98..1ea173075 100644
--- a/methods/aptmethod.h
+++ b/methods/aptmethod.h
@@ -514,6 +514,17 @@ protected:
return part;
}
+ static std::string CombineWithAlternatePath(std::string Path, std::string Change)
+ {
+ while (APT::String::Startswith(Change, "../"))
+ {
+ Path.erase(Path.find_last_not_of('/'));
+ Path = flNotFile(Path);
+ Change.erase(0, 3);
+ }
+ return flCombine(Path, Change);
+ }
+
aptMethod(std::string &&Binary, char const *const Ver, unsigned long const Flags) APT_NONNULL(3)
: pkgAcqMethod(Ver, Flags), aptConfigWrapperForMethods(Binary), Binary(std::move(Binary)), SeccompFlags(0)
{
diff --git a/methods/copy.cc b/methods/copy.cc
index 82eed150c..b32131a21 100644
--- a/methods/copy.cc
+++ b/methods/copy.cc
@@ -26,7 +26,7 @@
class CopyMethod : public aptMethod
{
- virtual bool Fetch(FetchItem *Itm) APT_OVERRIDE;
+ bool URIAcquire(std::string const &Message, FetchItem *Itm) APT_OVERRIDE;
public:
CopyMethod() : aptMethod("copy", "1.0", SingleInstance | SendConfig | SendURIEncoded)
@@ -36,55 +36,71 @@ class CopyMethod : public aptMethod
};
// CopyMethod::Fetch - Fetch a file /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-bool CopyMethod::Fetch(FetchItem *Itm)
+bool CopyMethod::URIAcquire(std::string const &Message, FetchItem *Itm)
{
- // this ensures that relative paths work in copy
- std::string const File = DecodeSendURI(Itm->Uri.substr(Itm->Uri.find(':')+1));
-
- // Stat the file and send a start message
- struct stat Buf;
- if (stat(File.c_str(),&Buf) != 0)
- return _error->Errno("stat",_("Failed to stat"));
+ struct FileCopyType {
+ std::string name;
+ struct stat stat{};
+ explicit FileCopyType(std::string &&file) : name{std::move(file)} {}
+ };
+ std::vector<FileCopyType> files;
+ // this ensures that relative paths work
+ files.emplace_back(DecodeSendURI(Itm->Uri.substr(Itm->Uri.find(':')+1)));
+ for (auto const &AltPath : VectorizeString(LookupTag(Message, "Alternate-Paths"), '\n'))
+ files.emplace_back(CombineWithAlternatePath(flNotFile(files[0].name), DecodeSendURI(AltPath)));
+ files.erase(std::remove_if(files.begin(), files.end(), [](auto &file)
+ { return stat(file.name.c_str(), &file.stat) != 0; }),
+ files.end());
+ if (files.empty())
+ return _error->Errno("copy-stat", _("Failed to stat"));
// Forumulate a result and send a start message
FetchResult Res;
- Res.Size = Buf.st_size;
Res.Filename = Itm->DestFile;
- Res.LastModified = Buf.st_mtime;
Res.IMSHit = false;
- URIStart(Res);
- // just calc the hashes if the source and destination are identical
- if (File == Itm->DestFile || Itm->DestFile == "/dev/null")
+ for (auto const &File : files)
{
+ Res.Size = File.stat.st_size;
+ Res.LastModified = File.stat.st_mtime;
+
+ // just calc the hashes if the source and destination are identical
+ if (Itm->DestFile == "/dev/null" || File.name == Itm->DestFile)
+ {
+ URIStart(Res);
+ CalculateHashes(Itm, Res);
+ URIDone(Res);
+ return true;
+ }
+
+ FileFd From(File.name, FileFd::ReadOnly);
+ FileFd To(Itm->DestFile, FileFd::WriteAtomic);
+ To.EraseOnFailure();
+ if (not From.IsOpen() || not To.IsOpen())
+ continue;
+
+ // Copy the file
+ URIStart(Res);
+ if (not CopyFile(From, To))
+ {
+ To.OpFail();
+ continue;
+ }
+ From.Close();
+ To.Close();
+
CalculateHashes(Itm, Res);
- URIDone(Res);
- return true;
- }
+ if (not Itm->ExpectedHashes.empty() && Itm->ExpectedHashes != Res.Hashes)
+ continue;
- // See if the file exists
- FileFd From(File,FileFd::ReadOnly);
- FileFd To(Itm->DestFile,FileFd::WriteAtomic);
- To.EraseOnFailure();
+ if (not TransferModificationTimes(File.name.c_str(), Res.Filename.c_str(), Res.LastModified))
+ continue;
- // Copy the file
- if (CopyFile(From,To) == false)
- {
- To.OpFail();
- return false;
+ URIDone(Res);
+ return true;
}
- From.Close();
- To.Close();
-
- if (TransferModificationTimes(File.c_str(), Res.Filename.c_str(), Res.LastModified) == false)
- return false;
-
- CalculateHashes(Itm, Res);
- URIDone(Res);
- return true;
+ return false;
}
/*}}}*/
diff --git a/methods/file.cc b/methods/file.cc
index ff93f83d0..279711195 100644
--- a/methods/file.cc
+++ b/methods/file.cc
@@ -29,7 +29,7 @@
class FileMethod : public aptMethod
{
- virtual bool Fetch(FetchItem *Itm) APT_OVERRIDE;
+ bool URIAcquire(std::string const &Message, FetchItem *Itm) APT_OVERRIDE;
public:
FileMethod() : aptMethod("file", "1.0", SingleInstance | SendConfig | LocalOnly | SendURIEncoded)
@@ -41,88 +41,101 @@ class FileMethod : public aptMethod
// FileMethod::Fetch - Fetch a file /*{{{*/
// ---------------------------------------------------------------------
/* */
-bool FileMethod::Fetch(FetchItem *Itm)
+bool FileMethod::URIAcquire(std::string const &Message, FetchItem *Itm)
{
URI Get(Itm->Uri);
- std::string const File = DecodeSendURI(Get.Path);
- FetchResult Res;
if (Get.Host.empty() == false)
return _error->Error(_("Invalid URI, local URIS must not start with //"));
- struct stat Buf;
+ std::vector<std::string> Files;
+ Files.emplace_back(DecodeSendURI(Get.Path));
+ for (auto const &AltPath : VectorizeString(LookupTag(Message, "Alternate-Paths"), '\n'))
+ Files.emplace_back(CombineWithAlternatePath(flNotFile(Files[0]), DecodeSendURI(AltPath)));
+
+ FetchResult Res;
// deal with destination files which might linger around
- if (lstat(Itm->DestFile.c_str(), &Buf) == 0)
+ struct stat Buf;
+ if (lstat(Itm->DestFile.c_str(), &Buf) == 0 && S_ISLNK(Buf.st_mode) && Buf.st_size > 0)
+ {
+ char name[Buf.st_size + 1];
+ if (ssize_t const sp = readlink(Itm->DestFile.c_str(), name, Buf.st_size); sp == -1)
+ {
+ Itm->LastModified = 0;
+ RemoveFile("file", Itm->DestFile);
+ }
+ }
+
+ int olderrno = 0;
+ // See if the file exists
+ for (auto const &File : Files)
{
- if ((Buf.st_mode & S_IFREG) != 0)
+ if (stat(File.c_str(), &Buf) == 0)
{
+ Res.Size = Buf.st_size;
+ Res.Filename = File;
+ Res.LastModified = Buf.st_mtime;
+ Res.IMSHit = false;
if (Itm->LastModified == Buf.st_mtime && Itm->LastModified != 0)
{
- if (Itm->ExpectedHashes.VerifyFile(File))
- {
- Res.Filename = Itm->DestFile;
+ auto const filesize = Itm->ExpectedHashes.FileSize();
+ if (filesize != 0 && filesize == Res.Size)
Res.IMSHit = true;
- }
}
+ break;
}
+ if (olderrno == 0)
+ olderrno = errno;
}
- if (Res.IMSHit != true)
- RemoveFile("file", Itm->DestFile);
- int olderrno = 0;
- // See if the file exists
- if (stat(File.c_str(),&Buf) == 0)
+ if (not Res.IMSHit)
{
- Res.Size = Buf.st_size;
- Res.Filename = File;
- Res.LastModified = Buf.st_mtime;
- Res.IMSHit = false;
- if (Itm->LastModified == Buf.st_mtime && Itm->LastModified != 0)
+ RemoveFile("file", Itm->DestFile);
+ if (not Res.Filename.empty())
{
- unsigned long long const filesize = Itm->ExpectedHashes.FileSize();
- if (filesize != 0 && filesize == Res.Size)
- Res.IMSHit = true;
+ URIStart(Res);
+ CalculateHashes(Itm, Res);
}
-
- CalculateHashes(Itm, Res);
}
- else
- olderrno = errno;
- if (Res.IMSHit == false)
- URIStart(Res);
// See if the uncompressed file exists and reuse it
FetchResult AltRes;
- AltRes.Filename.clear();
- std::vector<std::string> extensions = APT::Configuration::getCompressorExtensions();
- for (std::vector<std::string>::const_iterator ext = extensions.begin(); ext != extensions.end(); ++ext)
+ for (auto const &File : Files)
{
- if (APT::String::Endswith(File, *ext) == true)
+ for (const auto &ext : APT::Configuration::getCompressorExtensions())
{
- std::string const unfile = File.substr(0, File.length() - ext->length());
- if (stat(unfile.c_str(),&Buf) == 0)
+ if (APT::String::Endswith(File, ext))
{
- AltRes.Size = Buf.st_size;
- AltRes.Filename = unfile;
- AltRes.LastModified = Buf.st_mtime;
- AltRes.IMSHit = false;
- if (Itm->LastModified == Buf.st_mtime && Itm->LastModified != 0)
- AltRes.IMSHit = true;
- if (Res.Filename.empty())
- CalculateHashes(Itm, AltRes);
- break;
+ std::string const unfile = File.substr(0, File.length() - ext.length());
+ if (stat(unfile.c_str(), &Buf) == 0)
+ {
+ AltRes.Size = Buf.st_size;
+ AltRes.Filename = unfile;
+ AltRes.LastModified = Buf.st_mtime;
+ AltRes.IMSHit = false;
+ if (Itm->LastModified == Buf.st_mtime && Itm->LastModified != 0)
+ AltRes.IMSHit = true;
+ if (Res.Filename.empty())
+ {
+ URIStart(Res);
+ CalculateHashes(Itm, AltRes);
+ }
+ break;
+ }
+ // no break here as we could have situations similar to '.gz' vs '.tar.gz' here
}
- // no break here as we could have situations similar to '.gz' vs '.tar.gz' here
}
+ if (not AltRes.Filename.empty())
+ break;
}
- if (AltRes.Filename.empty() == false)
+ if (not AltRes.Filename.empty())
URIDone(Res,&AltRes);
- else if (Res.Filename.empty() == false)
+ else if (not Res.Filename.empty())
URIDone(Res);
else
{
errno = olderrno;
- return _error->Errno(File.c_str(), _("File not found"));
+ return _error->Errno(Files[0].c_str(), _("File not found"));
}
return true;