9346bc6c6ad08cfa58bf85fa2492c84ac5ca7543
[platform/core/system/upgrade.git] / src / upgrade-apply-deltafs / engine / SS_PatchDelta.c
1 /*
2  * upgrade-apply-deltafs
3  *
4  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
5  *
6  * Licensed under the Apache License, Version 2.0 (the License);
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *       http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 #include <errno.h>
20 #include <libgen.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/stat.h>
25 #include <sys/statfs.h>
26 #include <sys/types.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include "sha1.h"
30 #include "SS_PatchDelta.h"
31 #include "fota_common.h"
32 #include "SS_Engine_Errors.h"
33 #include "SS_Common.h"
34 #include "patch_helper.h"
35
36 extern void *SS_Malloc(unsigned int size);
37
38 typedef struct {
39         unsigned char *buffer;
40         ssize_t size;
41         ssize_t pos;
42 } MemorySinkInfo;
43
44 ssize_t ss_fileSink(unsigned char *data, ssize_t len, void *token)
45 {
46         int ss_fd = *(int *)token;
47         char buf[256];
48         ssize_t done = 0;
49         ssize_t wrote;
50         while (done < (ssize_t) len) {
51                 wrote = write(ss_fd, data + done, len - done);
52                 if (wrote <= 0) {
53                         if (errno == EINTR || errno == EAGAIN)
54                                 continue;          // try again
55                 strerror_r(errno, buf, sizeof(buf));
56                         LOGE("error writing %d bytes: %s\n", (int)(len - done), buf);
57                         return done;
58                 }
59                 done += wrote;
60         }
61         return done;
62 }
63
64 // Take a string 'str' of 40 hex digits and parse it into the 20
65 // byte array 'digest'.  'str' may contain only the digest or be of
66 // the form "<digest>:<anything>".  Return 0 on success, -1 on any
67 // error.
68 int ParseSha1(const char *str, uint8_t * digest)
69 {
70         int i;
71         const char *ps = str;
72         uint8_t *pd = digest;
73         for (i = 0; i < SHA_DIGEST_SIZE * 2; ++i, ++ps) {
74                 int digit;
75                 if (*ps >= '0' && *ps <= '9')
76                         digit = *ps - '0';
77                 else if (*ps >= 'a' && *ps <= 'f')
78                         digit = *ps - 'a' + 10;
79                 else if (*ps >= 'A' && *ps <= 'F')
80                         digit = *ps - 'A' + 10;
81                 else
82                         return -1;
83                 if (i % 2 == 0) {
84                         *pd = digit << 4;
85                 } else {
86                         *pd |= digit;
87                         ++pd;
88                 }
89         }
90         if (*ps != '\0')
91                 return -1;
92         return 0;
93 }
94
95 int SS_LoadFile(const char *filename, FileInfo * file)
96 {
97         char buf[256];
98
99         file->data = NULL;
100         //LOGL(LOG_SSENGINE,"SS_LoadFile --- [File name %s]\n",filename);
101
102         if (stat(filename, &file->st) != 0) {
103                 strerror_r(errno, buf, sizeof(buf));
104                 LOGE("failed to stat \"%s\": %s\n", filename, buf);
105                 return -1;
106         }
107
108         file->size = file->st.st_size;
109         file->data = SS_Malloc(file->size);
110         if (!file->data) {
111                 strerror_r(errno, buf, sizeof(buf));
112                 LOGE("failed to allocate memory for \"%s\": %s\n", filename, buf);
113                 return -1;
114         }
115
116         FILE *f = fopen(filename, "rb");
117         if (f == NULL) {
118                 strerror_r(errno, buf, sizeof(buf));
119                 LOGE("failed to open \"%s\": %s\n", filename, buf);
120                 SS_Free(file->data);
121                 file->data = NULL;
122                 return -1;
123         }
124
125         ssize_t bytes_read = fread(file->data, 1, file->size, f);
126         if (bytes_read != file->size) {
127                 LOGE("short read of \"%s\" (%ld bytes of %ld)\n", filename, (long)bytes_read, (long)file->size);
128                 SS_Free(file->data);
129                 file->data = NULL;
130                 fclose(f);
131                 return -1;
132         }
133         fclose(f);
134         //LOGL(LOG_SSENGINE,"SS_LoadFile --- [bytes_read %d]\n",bytes_read);
135         SHA1(file->sha1, file->data, file->size);
136         return 0;
137 }
138
139 extern int gvalid_session;
140 static void create_dir(char *pathname, int mode)
141 {
142         char *p;
143         int r;
144
145         /* Strip trailing '/' */
146         if (pathname[strlen(pathname) - 1] == '/')
147                 pathname[strlen(pathname) - 1] = '\0';
148
149         /* Try creating the directory. */
150         r = mkdir(pathname, mode);
151
152         if (r != 0) {
153                 /* On failure, try creating parent directory. */
154                 p = strrchr(pathname, '/');
155                 if (p != NULL) {
156                         *p = '\0';
157                         create_dir(pathname, 0755);
158                         *p = '/';
159                         r = mkdir(pathname, mode);
160                 }
161         }
162         if (r != 0) {
163                 if (r != EEXIST && r != -1)
164                         LOG("Could not create directory [%s] Error[%d]\n", pathname, r);
165         }
166 }
167
168 /*!
169  *********************************************************************************
170  *                                       SS_UpdateDeltaFS
171  *********************************************************************************
172  *
173  * @brief
174  *      This is used to apply patch for a file during delta FS upgrade
175  *
176  *
177  *      @param
178  *
179  *      @return                         0 - in case of success
180  *                                              1 - in case of error during patch application
181  *
182  *********************************************************************************
183  */
184
185 int SS_UpdateDeltaFS(const char *source_filename, const char *target_filename,
186                                          const char *source_sha1_str, const char *target_sha1_str, int patch_data_size, TAR* tar_file)
187 {
188         uint8_t target_sha1[SHA_DIGEST_SIZE] = { 0, };
189         SHA1_CTX ctx1;
190         int output;
191         int retry = 1;
192         char *outname = NULL;
193         int result = 0;
194         char buf[256];
195
196         if (ParseSha1(target_sha1_str, target_sha1) != 0) {
197                 LOGE("failed to parse tgt-sha1 \"%s\"\n", target_sha1_str);
198                 return E_SS_FAILURE;
199         }
200
201         do {
202                 int enough_space = 0;
203                 size_t free_space = 0;
204
205                 if (retry > 0) {
206                         SS_GetAvailableFreeSpace(source_filename, &free_space);
207                         enough_space = (free_space > (256 << 10)) &&            // 256k (two-block) minimum
208                                 (free_space > (patch_data_size * 3 / 2));          // 50% margin of error
209                 }
210                 if (!enough_space) {
211                         LOGL(LOG_SSENGINE, "For %s: free space %ld bytes; enough %d\n", source_filename, (long)free_space,
212                                  enough_space);
213                         retry = 0;
214                         unlink(source_filename);
215                 }
216                 //LOGL(LOG_SSENGINE, "For %s: target %ld bytes; free space %ld bytes; enough %d\n",
217                 // source_filename, (long)patch_data_size, (long)free_space, enough_space);
218                 //LOGL(LOG_SSENGINE,"Generate Target Space availabitiy [%d]\n", enough_space);
219
220                 output = -1;
221                 outname = NULL;
222
223                 {
224                         // We write the decoded output to "<tgt-file>.patch".
225                         //allocate some extra space to allow for concatinating ".patch" with the name
226                         outname = (char *)SS_Malloc(strlen(target_filename) + 10);
227                         if (outname == NULL) {
228                                 LOGE("SS_Malloc failed!!\n");
229                                 return E_SS_FAILURE;
230                         }
231                         snprintf(outname, strlen(target_filename) + 10,
232                                         "%s.patch", target_filename);
233
234                         output = open(outname, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
235                         if (output < 0) {
236                                 if (errno == 2) {
237                                         char *dir_path = strrchr(outname, '/');
238                                         *dir_path = '\0';
239                                         // need to create directory as the target may be different from source
240                                         LOGL(LOG_SSENGINE, "need to create directory [%s]\n", outname);
241                                         create_dir(outname, 0755);
242                                         *dir_path = '/';
243                                         output = open(outname, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
244                                         if (output < 0) {
245                                                 strerror_r(errno, buf, sizeof(buf));
246                                                 LOGE("failed to open output file %s: %s\n", outname, buf);
247                                                 result = E_SS_FAILURE;
248                                                 goto exit;
249                                         }
250                                 }
251                         }
252                 }
253                 result = apply_patch_brotli((char *)source_filename, outname, tar_file, -1, &free_space);
254                 if (output >= 0) {
255                         fsync(output);
256                         close(output);
257                 }
258
259                 if (result != S_SS_SUCCESS) {
260                         if (retry == 0) {
261                                 LOGE("applying patch failed result : [%d]\n", result);
262                                 SS_SetUpgradeState(E_SS_FSUPDATEFAILED);
263                                 result = E_SS_FAILURE;
264                                 goto exit;
265                         } else {
266                                 LOGE("applying patch failed; retrying\n");
267                                 SS_Free(outname);//wgid: 20739
268                         }
269                 } else {
270                         // succeeded; no need to retry
271                         break;
272                 }
273         } while (retry-- > 0);
274
275         unsigned char current_target_sha1[SHA_DIGEST_SIZE] = { 0, };
276         SHA1Init(&ctx1);
277         int fd = open(outname, O_RDONLY);
278         if (fd == -1) {
279                 LOGE("cannot open %s to verify SHA1\n", outname);
280                 result = E_SS_FAILURE;
281                 goto exit;
282         }
283         unsigned char buff[4096];
284         for (;;) {
285                 int res = read(fd, buff, 4096);
286                 if (res == -1) {
287                         LOGE("cannot read %s to verify SHA1\n", outname);
288                         close(fd);
289                         result = E_SS_FAILURE;
290                         goto exit;
291                 }
292                 if (res == 0)
293                         break;
294
295                 SHA1Update(&ctx1, buff, res);
296         }
297         close(fd);
298
299         SHA1Final(current_target_sha1, &ctx1);
300         if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
301                 LOGE("patch did not produce expected sha1\n");
302                 SS_SetUpgradeState(E_SS_FSSHA_MISMATCH);
303                 result = E_SS_FAILURE;
304                 goto exit;
305         }
306
307         // Finally, rename the .patch file to replace the target file.
308 #ifdef ENHANCED_BSDIFF
309         if (SS_rename1(outname, target_filename) != 0) {
310                 strerror_r(errno, buf, sizeof(buf));
311                 LOGE("rename of .patch to \"%s\" failed: %s\n", target_filename, buf);
312                 SS_SetUpgradeState(E_SS_FSUPDATEFAILED);
313                 result = E_SS_FAILURE;
314                 goto exit;
315         }
316 #else
317         if (rename(outname, target_filename) != 0) {
318                 strerror_r(errno, buf, sizeof(buf));
319                 LOGE("rename of .patch to \"%s\" failed: %s\n", target_filename, buf);
320                 SS_SetUpgradeState(E_SS_FSUPDATEFAILED);
321                 result = E_SS_FAILURE;
322                 goto exit;
323         }
324         //remove source file if target is not same
325         if (strcmp(source_filename, target_filename) != 0)
326                 unlink(source_filename);
327 #endif
328 exit:
329         SS_Free(outname);
330
331         return result;
332 }