blob: c70867ecdfccb41996d4b977e80865af2000023a [file] [log] [blame]
adlr@google.com3defe6a2009-12-04 20:57:17 +00001// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/install_action.h"
6#include <errno.h>
7#include <vector>
8#include <gflags/gflags.h>
9#include "update_engine/filesystem_iterator.h"
10#include "update_engine/gzip.h"
11#include "update_engine/subprocess.h"
12#include "update_engine/utils.h"
13
14DEFINE_string(mount_install_path, "",
15 "If set, the path to use when mounting the "
16 "destination device during install");
17
18using std::vector;
19
20namespace chromeos_update_engine {
21
22namespace {
23const string kBspatchPath = "/usr/bin/bspatch";
24}
25
26void InstallAction::PerformAction() {
27 ScopedActionCompleter completer(processor_, this);
28 // For now, do nothing other than pass what we need to to the output pipe
29 CHECK(HasInputObject());
30 const InstallPlan install_plan = GetInputObject();
31 if (HasOutputPipe())
32 SetOutputObject(install_plan.install_path);
33 if (install_plan.is_full_update) {
34 // No need to perform an install
35 completer.set_success(true);
36 return;
37 }
38 // We have a delta update.
39
40 // Open delta file
41 DeltaDiffParser parser(install_plan.download_path);
42 if (!parser.valid()) {
43 LOG(ERROR) << "Unable to open delta file";
44 return;
45 }
46
47 // Mount install fs
48 string mountpoint = FLAGS_mount_install_path;
49 if (mountpoint.empty()) {
50 // Set up dest_path_
51 char *mountpoint_temp = strdup("/tmp/install_mnt.XXXXXX");
52 CHECK(mountpoint_temp);
53 CHECK_EQ(mountpoint_temp, mkdtemp(mountpoint_temp));
54 CHECK_NE('\0', mountpoint_temp[0]);
55 mountpoint = mountpoint_temp;
56 free(mountpoint_temp);
57 }
58
59 TEST_AND_RETURN(utils::MountFilesystem(install_plan.install_path,
60 mountpoint));
61
62 // Automatically unmount the fs when this goes out of scope:
63 ScopedFilesystemUnmounter filesystem_unmounter(mountpoint);
64
65 {
Andrew de los Reyese5733992009-12-08 13:34:00 -080066 // Iterate through existing fs, deleting unneeded files
67 // Delete files that don't exist in the update, or exist but are
68 // hard links.
adlr@google.com3defe6a2009-12-04 20:57:17 +000069 FilesystemIterator iter(mountpoint,
70 utils::SetWithValue<string>("/lost+found"));
71 for (; !iter.IsEnd(); iter.Increment()) {
Andrew de los Reyese5733992009-12-08 13:34:00 -080072 if (!parser.ContainsPath(iter.GetPartialPath()) ||
73 parser.GetFileAtPath(iter.GetPartialPath()).has_hardlink_path()) {
adlr@google.com3defe6a2009-12-04 20:57:17 +000074 VLOG(1) << "install removing local path: " << iter.GetFullPath();
75 TEST_AND_RETURN(utils::RecursiveUnlinkDir(iter.GetFullPath()));
76 }
77 }
78 TEST_AND_RETURN(!iter.IsErr());
79 }
80
81 // iterate through delta metadata, writing files
82 DeltaDiffParserIterator iter = parser.Begin();
83 for (; iter != parser.End(); iter.Increment()) {
84 const DeltaArchiveManifest_File& file = iter.GetFile();
85 VLOG(1) << "Installing file: " << iter.path();
86 TEST_AND_RETURN(InstallFile(mountpoint, file, iter.path(), parser));
87 }
88
89 completer.set_success(true);
90}
91
92bool InstallAction::InstallFile(const std::string& mountpoint,
93 const DeltaArchiveManifest_File& file,
94 const std::string& path,
95 const DeltaDiffParser& parser) const {
96 // See what's already there
97 struct stat existing_stbuf;
98 int result = lstat((mountpoint + path).c_str(), &existing_stbuf);
99 TEST_AND_RETURN_FALSE_ERRNO((result == 0) || (errno == ENOENT));
100 bool exists = (result == 0);
101 // Create the proper file
Andrew de los Reyese5733992009-12-08 13:34:00 -0800102 if (file.has_hardlink_path()) {
103 TEST_AND_RETURN_FALSE(file.has_hardlink_path());
104 TEST_AND_RETURN_FALSE_ERRNO(link(
105 (mountpoint + file.hardlink_path()).c_str(),
106 (mountpoint + path).c_str()) == 0);
107 } else if (S_ISDIR(file.mode())) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000108 if (!exists) {
109 TEST_AND_RETURN_FALSE_ERRNO(
110 (mkdir((mountpoint + path).c_str(), file.mode())) == 0);
111 }
112 } else if (S_ISLNK(file.mode())) {
113 InstallFileSymlink(mountpoint, file, path, parser, exists);
114 } else if (S_ISCHR(file.mode()) ||
115 S_ISBLK(file.mode()) ||
116 S_ISFIFO(file.mode()) ||
117 S_ISSOCK(file.mode())) {
118 InstallFileSpecialFile(mountpoint, file, path, parser, exists);
119 } else if (S_ISREG(file.mode())) {
120 InstallFileRegularFile(mountpoint, file, path, parser, exists);
121 } else {
122 // unknown mode type
123 TEST_AND_RETURN_FALSE(false);
124 }
125
126 // chmod/chown new file
127 if (!S_ISLNK(file.mode()))
128 TEST_AND_RETURN_FALSE_ERRNO(chmod((mountpoint + path).c_str(), file.mode())
129 == 0);
130 TEST_AND_RETURN_FALSE(file.has_uid() && file.has_gid());
131 TEST_AND_RETURN_FALSE_ERRNO(lchown((mountpoint + path).c_str(),
132 file.uid(), file.gid()) == 0);
133 return true;
134}
135
136bool InstallAction::InstallFileRegularFile(
137 const std::string& mountpoint,
138 const DeltaArchiveManifest_File& file,
139 const std::string& path,
140 const DeltaDiffParser& parser,
141 const bool exists) const {
142 if (!file.has_data_format())
143 return true;
144 TEST_AND_RETURN_FALSE(file.has_data_offset() && file.has_data_length());
145 if (file.data_format() == DeltaArchiveManifest_File_DataFormat_BSDIFF) {
146 // Expand with bspatch
147 string patch_path = utils::TempFilename(mountpoint + path + ".XXXXXX");
148 TEST_AND_RETURN_FALSE(file.has_data_length());
149 TEST_AND_RETURN_FALSE(parser.CopyDataToFile(
150 file.data_offset(),
151 static_cast<off_t>(file.data_length()), false,
152 patch_path));
153 string output_path = utils::TempFilename(mountpoint + path + ".XXXXXX");
154 int rc = 1;
155 vector<string> cmd;
156 cmd.push_back(kBspatchPath);
157 cmd.push_back(mountpoint + path);
158 cmd.push_back(output_path);
159 cmd.push_back(patch_path);
160 TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &rc));
161 TEST_AND_RETURN_FALSE(rc == 0);
162 TEST_AND_RETURN_FALSE_ERRNO(rename(output_path.c_str(),
163 (mountpoint + path).c_str()) == 0);
164 TEST_AND_RETURN_FALSE_ERRNO(unlink(patch_path.c_str()) == 0);
165 } else {
166 // Expand full data, decompressing if necessary
167 TEST_AND_RETURN_FALSE((file.data_format() ==
168 DeltaArchiveManifest_File_DataFormat_FULL) ||
169 (file.data_format() ==
170 DeltaArchiveManifest_File_DataFormat_FULL_GZ));
171 if (exists)
172 TEST_AND_RETURN_FALSE_ERRNO(unlink((mountpoint + path).c_str()) == 0);
173 TEST_AND_RETURN_FALSE(file.has_data_length());
174 const bool gzipped = file.data_format() ==
175 DeltaArchiveManifest_File_DataFormat_FULL_GZ;
176 bool success =
177 parser.CopyDataToFile(file.data_offset(), file.data_length(),
178 gzipped,
179 mountpoint + path);
180 TEST_AND_RETURN_FALSE(success);
181 }
182 return true;
183}
184
185// char/block devices, fifos, and sockets:
186bool InstallAction::InstallFileSpecialFile(
187 const std::string& mountpoint,
188 const DeltaArchiveManifest_File& file,
189 const std::string& path,
190 const DeltaDiffParser& parser,
191 const bool exists) const {
192 if (exists)
193 TEST_AND_RETURN_FALSE(unlink((mountpoint + path).c_str()) == 0);
194 dev_t dev = 0;
195 if (S_ISCHR(file.mode()) || S_ISBLK(file.mode())) {
196 vector<char> dev_proto;
Andrew de los Reyese5733992009-12-08 13:34:00 -0800197 TEST_AND_RETURN_FALSE(file.has_data_format());
adlr@google.com3defe6a2009-12-04 20:57:17 +0000198 TEST_AND_RETURN_FALSE(parser.ReadDataVector(file.data_offset(),
199 file.data_length(),
200 &dev_proto));
201 if (file.data_format() == DeltaArchiveManifest_File_DataFormat_FULL_GZ) {
202 TEST_AND_RETURN_FALSE(file.has_data_length());
203 {
204 vector<char> decompressed_dev_proto;
205 TEST_AND_RETURN_FALSE(GzipDecompress(dev_proto,
206 &decompressed_dev_proto));
207 dev_proto = decompressed_dev_proto;
208 }
209 } else {
210 TEST_AND_RETURN_FALSE(file.data_format() ==
211 DeltaArchiveManifest_File_DataFormat_FULL);
212 }
213 LinuxDevice linux_device;
214 utils::HexDumpVector(dev_proto);
215 TEST_AND_RETURN_FALSE(linux_device.ParseFromArray(&dev_proto[0],
216 dev_proto.size()));
217 dev = makedev(linux_device.major(), linux_device.minor());
218 }
219 TEST_AND_RETURN_FALSE_ERRNO(mknod((mountpoint + path).c_str(),
220 file.mode(), dev) == 0);
221 return true;
222}
223// symlinks:
224bool InstallAction::InstallFileSymlink(const std::string& mountpoint,
225 const DeltaArchiveManifest_File& file,
226 const std::string& path,
227 const DeltaDiffParser& parser,
228 const bool exists) const {
229 // If there's no data, we leave the symlink as is
230 if (!file.has_data_format())
231 return true; // No changes needed
232 TEST_AND_RETURN_FALSE((file.data_format() ==
233 DeltaArchiveManifest_File_DataFormat_FULL) ||
234 (file.data_format() ==
235 DeltaArchiveManifest_File_DataFormat_FULL_GZ));
236 TEST_AND_RETURN_FALSE(file.has_data_offset() && file.has_data_length());
237 // We have data, and thus use it to create a symlink.
238 // First delete any existing symlink:
239 if (exists)
240 TEST_AND_RETURN_FALSE_ERRNO(unlink((mountpoint + path).c_str()) == 0);
241 vector<char> symlink_data;
242 TEST_AND_RETURN_FALSE(parser.ReadDataVector(file.data_offset(),
243 file.data_length(),
244 &symlink_data));
245 if (file.data_format() == DeltaArchiveManifest_File_DataFormat_FULL_GZ) {
246 vector<char> decompressed_symlink_data;
247 TEST_AND_RETURN_FALSE(GzipDecompress(symlink_data,
248 &decompressed_symlink_data));
249 symlink_data = decompressed_symlink_data;
250 }
251 symlink_data.push_back('\0');
252 TEST_AND_RETURN_FALSE_ERRNO(symlink(&symlink_data[0],
253 (mountpoint + path).c_str()) == 0);
254 return true;
255}
256
257
258} // namespace chromeos_update_engine