summaryrefslogtreecommitdiff
path: root/methods/debdelta.cc
blob: 81d9ed8882dbf73bbbd3cc28cc482bf5b5942c06 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// Includes									/*{{{*/
#include <apt-pkg/acquire-item.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/mmap.h>
#include <apt-pkg/error.h>
#include <apt-pkg/acquire-method.h>
#include <apt-pkg/strutl.h>
#include <apt-pkg/hashes.h>
#include <apt-pkg/init.h>

#include <sys/stat.h>
#include <sys/uio.h>
#include <unistd.h>
#include <utime.h>
#include <stdio.h>
#include <errno.h>
#include <zlib.h>
#include <apti18n.h>

/*}}}*/
/** \brief DebdeltaMethod - TODO: say something about debdelta here!
 * */
class DebdeltaMethod : public pkgAcqMethod {
   bool Debug;  

protected:
   // the main(i.e. most important) method of the debdelta method.
   virtual bool Fetch(FetchItem *Itm);
public:
   DebdeltaMethod() : pkgAcqMethod("1.1", SingleInstance | SendConfig) {};
   string GetNewPackageName(string debdeltaName);
};

bool DebdeltaMethod::Fetch(FetchItem *Itm)						/*{{{*/
{
   /// Testing only...
   FetchResult ResTest;
   ResTest.Filename = "/home/ishan/devel/apt/testrepo/binary/gcc-4.6-base_4.6.0-7_amd64.deb";
   URIDone(ResTest);
   return true; 
   ///
   Debug = _config->FindB("Debug::pkgAcquire::RRed", false);
   URI U(Itm->Uri);
   string OldDebFile = Itm->DestFile;
   string DebDeltaFile = U.Path;
   FetchResult Res;
   if (flExtension(OldDebFile) != ".deb" || !FileExists(OldDebFile))
      OldDebFile = "/";
   if (!FileExists(DebDeltaFile))
      return _error->Error("[Debdelta] Could not find a debdelta file.");
   string NewDeb = GetNewPackageName(DebDeltaFile);
   Itm->DestFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + NewDeb;
   if (FileExists(Itm->DestFile))
      return _error->Error("[Debdelta] New .deb already exists.");
   
   pid_t Process = ExecFork();      
   if (Process == 0)
   {
      const char* Args[6] = {0};
      Args[0] = "/usr/bin/debpatch";
      if (!FileExists(Args[0]))
	 return _error->Error("[Debdelta] Could not find debpatch.");
      Args[1] = "-A";
      Args[2] = DebDeltaFile.c_str();
      Args[3] = OldDebFile.c_str();
      Args[4] = Itm->DestFile.c_str();
      if (Debug == true)
      {
	 std::cerr << "[Debdelta] Command:" << std::endl;
	 std::cerr << Args[0] << " " << Args[1] << " " << Args[2] << " "
		   << Args[3] << " " << Args[4] << std::endl;
      }
      std::cerr << "[Debdelta] Patching " << NewDeb << "..." << std::endl;
      execv(Args[0], (char **)Args);
   }
   if (ExecWait(Process, "debpatch"))
   {
      if (!FileExists(Itm->DestFile))
	 return _error->Error("[Debdelta] Failed to patch %s", Itm->DestFile.c_str());
      // move the .deb to Dir::Cache::Archives
      OldDebFile = _config->FindDir("Dir::Cache::Archives") + NewDeb;
      Rename(Itm->DestFile, OldDebFile);
      Itm->DestFile = OldDebFile;
      Res.Filename = Itm->DestFile;
      URIDone(Res);
      return true;
   }
   Itm->DestFile = OldDebFile;
   
   return false;
}

/**
 * \brief Receives a debdelta file name in the form of path/P_O_N_A.debdelta and constructs the name
 * of the deb with the newer version.
 * @param debdeltaName the name of the debdelta file.
 * @return path/P_N_A.deb
 */
string DebdeltaMethod::GetNewPackageName(string DebdeltaName)
{
   int Slashpos = DebdeltaName.rfind("/", DebdeltaName.length() - 1);
   string Path = DebdeltaName.substr(0, Slashpos + 1);
   DebdeltaName = DebdeltaName.substr(Slashpos + 1, DebdeltaName.length() - 1);
   int NewBegin = DebdeltaName.find("_", 0);
   string PkgName = DebdeltaName.substr(0, NewBegin); 
   NewBegin = DebdeltaName.find("_", NewBegin + 1);
   int NewEnd = DebdeltaName.find("_", NewBegin + 1);
   string NewVersion = DebdeltaName.substr(NewBegin + 1, NewEnd - NewBegin - 1);
   string Arch = DebdeltaName.substr(NewEnd + 1, DebdeltaName.find(".", NewEnd + 1) - NewEnd - 1);
   return PkgName + "_" + NewVersion + "_" + Arch + ".deb";
}

/*}}}*/
/** \brief Wrapper class for testing debdelta */					/*{{{*/
class TestDebdeltaMethod : public DebdeltaMethod {
public:
   /** \brief Run debdelta in debug test mode
    *
    *  This method can be used to run the debdelta method outside
    *  of the "normal" acquire environment for easier testing.
    *
    *  \param base basename of all files involved in this debdelta test
    */
   bool Run(char const *debFile, char const *debdeltaFile)
   {
      if (pkgInitConfig(*_config) == false ||
          pkgInitSystem(*_config,_system) == false)
      {
	 std::cerr << "[Debdelta] E: Could not initialize the system/configuration." << std::endl;
	 _error->DumpErrors();
	 return 100;
      }
      _config->CndSet("Debug::pkgAcquire::Debdetla", "true");
      FetchItem *test = new FetchItem;
      test->DestFile = debFile;
      test->Uri = debdeltaFile;
      test->FailIgnore = false;
      test->IndexFile = false;
      test->Next = 0;
      if (Fetch(test))
      {
	 std::cout << "Result-Deb: " << test->DestFile << std::endl;
	 return true;
      }
      return false;   
   }
};

/*}}}*/
/** \brief Starter for the debdelta method (or its test method)			{{{
 *
 *  Used without parameters is the normal behavior for methods for
 *  the APT acquire system. While this works great for the acquire system
 *  it is very hard to test the method and therefore the method also
 *  accepts one parameter which will switch it directly to debug test mode:
 */
int main(int argc, char *argv[])
{
   if (argc <= 1)
   {
      DebdeltaMethod Mth;
      return Mth.Run();
   }
   else
   {
      TestDebdeltaMethod Mth;
      bool result = Mth.Run(argv[1], argv[2]);
      _error->DumpErrors();
      return result;
   }
}
/*}}}*/