Jackeagle | 57acbdb | 2019-05-06 19:59:01 +0200 | [diff] [blame^] | 1 | From e9a0844ed2609aba7351c45bc5301437aeb1eb25 Mon Sep 17 00:00:00 2001 |
Jackeagle | 1cba413 | 2018-12-19 17:08:24 +0100 | [diff] [blame] | 2 | From: Jackeagle <jackeagle102@gmail.com> |
| 3 | Date: Wed, 19 Dec 2018 17:02:18 +0100 |
| 4 | Subject: [PATCH 7/8] Revert "system: vold: Remove crypto block device |
| 5 | creation" |
| 6 | |
| 7 | This reverts commit 4d404ad5154ce73bafa3820b72f2be397b79b628. |
| 8 | --- |
| 9 | Android.bp | 3 -- |
| 10 | EncryptInplace.cpp | 77 ---------------------------------------------- |
| 11 | VoldUtil.h | 3 -- |
| 12 | cryptfs.cpp | 67 ---------------------------------------- |
| 13 | 4 files changed, 150 deletions(-) |
| 14 | |
| 15 | diff --git a/Android.bp b/Android.bp |
| 16 | index 416b493..ffb139e 100644 |
| 17 | --- a/Android.bp |
| 18 | +++ b/Android.bp |
| 19 | @@ -145,9 +145,6 @@ cc_library_static { |
| 20 | header_libs: ["libcryptfs_hw_headers"], |
| 21 | shared_libs: ["libcryptfs_hw"], |
| 22 | }, |
| 23 | - device_support_hwfde_perf: { |
| 24 | - cflags: ["-DCONFIG_HW_DISK_ENCRYPT_PERF"], |
| 25 | - }, |
| 26 | }, |
| 27 | } |
| 28 | |
| 29 | diff --git a/EncryptInplace.cpp b/EncryptInplace.cpp |
| 30 | index d46d23e..6462dbf 100644 |
| 31 | --- a/EncryptInplace.cpp |
| 32 | +++ b/EncryptInplace.cpp |
| 33 | @@ -32,9 +32,6 @@ |
| 34 | #include <android-base/logging.h> |
| 35 | #include <android-base/properties.h> |
| 36 | |
| 37 | -#ifdef CONFIG_HW_DISK_ENCRYPTION |
| 38 | -#include "cryptfs_hw.h" |
| 39 | -#endif |
| 40 | // HORRIBLE HACK, FIXME |
| 41 | #include "cryptfs.h" |
| 42 | |
| 43 | @@ -285,27 +282,6 @@ static int cryptfs_enable_inplace_ext4(char* crypto_blkdev, char* real_blkdev, o |
| 44 | } |
| 45 | |
| 46 | LOG(DEBUG) << "Opening" << crypto_blkdev; |
| 47 | -#if defined(CONFIG_HW_DISK_ENCRYPTION) && defined(CONFIG_HW_DISK_ENCRYPT_PERF) |
| 48 | - if (is_ice_enabled()) |
| 49 | - data.cryptofd = data.realfd; |
| 50 | - else { |
| 51 | - // Wait until the block device appears. Re-use the mount retry values since it is reasonable. |
| 52 | - while ((data.cryptofd = open(crypto_blkdev, O_WRONLY|O_CLOEXEC)) < 0) { |
| 53 | - if (--retries) { |
| 54 | - PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev |
| 55 | - << " for ext4 inplace encrypt. err=" << errno |
| 56 | - << "(" << strerror(errno) << "), retrying"; |
| 57 | - sleep(RETRY_MOUNT_DELAY_SECONDS); |
| 58 | - } else { |
| 59 | - PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev |
| 60 | - << " for ext4 inplace encrypt. err=" << errno |
| 61 | - << "(" << strerror(errno) << "), retrying"; |
| 62 | - rc = ENABLE_INPLACE_ERR_DEV; |
| 63 | - goto errout; |
| 64 | - } |
| 65 | - } |
| 66 | - } |
| 67 | -#else |
| 68 | // Wait until the block device appears. Re-use the mount retry values since it is reasonable. |
| 69 | while ((data.cryptofd = open(crypto_blkdev, O_WRONLY|O_CLOEXEC)) < 0) { |
| 70 | if (--retries) { |
| 71 | @@ -319,7 +295,6 @@ static int cryptfs_enable_inplace_ext4(char* crypto_blkdev, char* real_blkdev, o |
| 72 | goto errout; |
| 73 | } |
| 74 | } |
| 75 | -#endif |
| 76 | |
| 77 | if (setjmp(setjmp_env)) { // NOLINT |
| 78 | LOG(ERROR) << "Reading ext4 extent caused an exception"; |
| 79 | @@ -365,12 +340,7 @@ static int cryptfs_enable_inplace_ext4(char* crypto_blkdev, char* real_blkdev, o |
| 80 | |
| 81 | errout: |
| 82 | close(data.realfd); |
| 83 | -#if defined(CONFIG_HW_DISK_ENCRYPTION) && defined(CONFIG_HW_DISK_ENCRYPT_PERF) |
| 84 | - if (!is_ice_enabled()) |
| 85 | - close(data.cryptofd); |
| 86 | -#else |
| 87 | close(data.cryptofd); |
| 88 | -#endif |
| 89 | |
| 90 | return rc; |
| 91 | } |
| 92 | @@ -446,26 +416,12 @@ static int cryptfs_enable_inplace_f2fs(char* crypto_blkdev, char* real_blkdev, o |
| 93 | PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for f2fs inplace encrypt"; |
| 94 | goto errout; |
| 95 | } |
| 96 | -#if defined(CONFIG_HW_DISK_ENCRYPTION) && defined(CONFIG_HW_DISK_ENCRYPT_PERF) |
| 97 | - if (is_ice_enabled()) |
| 98 | - data.cryptofd = data.realfd; |
| 99 | - else { |
| 100 | - if ((data.cryptofd = open64(crypto_blkdev, O_WRONLY|O_CLOEXEC)) < 0) { |
| 101 | - PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev |
| 102 | - << " for f2fs inplace encrypt. err=" << errno |
| 103 | - << "(" << strerror(errno) << "), retrying"; |
| 104 | - rc = ENABLE_INPLACE_ERR_DEV; |
| 105 | - goto errout; |
| 106 | - } |
| 107 | - } |
| 108 | -#else |
| 109 | if ( (data.cryptofd = open64(crypto_blkdev, O_WRONLY|O_CLOEXEC)) < 0) { |
| 110 | PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev |
| 111 | << " for f2fs inplace encrypt"; |
| 112 | rc = ENABLE_INPLACE_ERR_DEV; |
| 113 | goto errout; |
| 114 | } |
| 115 | -#endif |
| 116 | |
| 117 | f2fs_info = generate_f2fs_info(data.realfd); |
| 118 | if (!f2fs_info) |
| 119 | @@ -509,12 +465,7 @@ errout: |
| 120 | free(f2fs_info); |
| 121 | free(data.buffer); |
| 122 | close(data.realfd); |
| 123 | -#if defined(CONFIG_HW_DISK_ENCRYPTION) && defined(CONFIG_HW_DISK_ENCRYPT_PERF) |
| 124 | - if (!is_ice_enabled()) |
| 125 | - close(data.cryptofd); |
| 126 | -#else |
| 127 | close(data.cryptofd); |
| 128 | -#endif |
| 129 | |
| 130 | return rc; |
| 131 | } |
| 132 | @@ -535,25 +486,11 @@ static int cryptfs_enable_inplace_full(char* crypto_blkdev, char* real_blkdev, o |
| 133 | return ENABLE_INPLACE_ERR_OTHER; |
| 134 | } |
| 135 | |
| 136 | -#if defined(CONFIG_HW_DISK_ENCRYPTION) && defined(CONFIG_HW_DISK_ENCRYPT_PERF) |
| 137 | - if (is_ice_enabled()) |
| 138 | - cryptofd = realfd; |
| 139 | - else { |
| 140 | - if ((cryptofd = open(crypto_blkdev, O_WRONLY|O_CLOEXEC)) < 0) { |
| 141 | - PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev |
| 142 | - << " for inplace encrypt. err=" << errno |
| 143 | - << "(" << strerror(errno) << "), retrying"; |
| 144 | - close(realfd); |
| 145 | - return ENABLE_INPLACE_ERR_DEV; |
| 146 | - } |
| 147 | - } |
| 148 | -#else |
| 149 | if ( (cryptofd = open(crypto_blkdev, O_WRONLY|O_CLOEXEC)) < 0) { |
| 150 | PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev << " for inplace encrypt"; |
| 151 | close(realfd); |
| 152 | return ENABLE_INPLACE_ERR_DEV; |
| 153 | } |
| 154 | -#endif |
| 155 | |
| 156 | /* This is pretty much a simple loop of reading 4K, and writing 4K. |
| 157 | * The size passed in is the number of 512 byte sectors in the filesystem. |
| 158 | @@ -574,19 +511,10 @@ static int cryptfs_enable_inplace_full(char* crypto_blkdev, char* real_blkdev, o |
| 159 | goto errout; |
| 160 | } |
| 161 | |
| 162 | -#if defined(CONFIG_HW_DISK_ENCRYPTION) && defined(CONFIG_HW_DISK_ENCRYPT_PERF) |
| 163 | - if (!is_ice_enabled()) { |
| 164 | - if (lseek64(cryptofd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) { |
| 165 | - PLOG(ERROR) << "Cannot seek to previously encrypted point on " << crypto_blkdev; |
| 166 | - goto errout; |
| 167 | - } |
| 168 | - } |
| 169 | -#else |
| 170 | if (lseek64(cryptofd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) { |
| 171 | PLOG(ERROR) << "Cannot seek to previously encrypted point on " << crypto_blkdev; |
| 172 | goto errout; |
| 173 | } |
| 174 | -#endif |
| 175 | |
| 176 | for (;i < size && i % CRYPT_SECTORS_PER_BUFSIZE != 0; ++i) { |
| 177 | if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) { |
| 178 | @@ -649,12 +577,7 @@ static int cryptfs_enable_inplace_full(char* crypto_blkdev, char* real_blkdev, o |
| 179 | |
| 180 | errout: |
| 181 | close(realfd); |
| 182 | -#if defined(CONFIG_HW_DISK_ENCRYPTION) && defined(CONFIG_HW_DISK_ENCRYPT_PERF) |
| 183 | - if (!is_ice_enabled()) |
| 184 | - close(cryptofd); |
| 185 | -#else |
| 186 | close(cryptofd); |
| 187 | -#endif |
| 188 | |
| 189 | return rc; |
| 190 | } |
| 191 | diff --git a/VoldUtil.h b/VoldUtil.h |
| 192 | index ac484d8..fd66672 100644 |
| 193 | --- a/VoldUtil.h |
| 194 | +++ b/VoldUtil.h |
| 195 | @@ -26,7 +26,4 @@ extern struct fstab *fstab_default; |
| 196 | |
| 197 | void get_blkdev_size(int fd, unsigned long* nr_sec); |
| 198 | |
| 199 | -#ifdef CONFIG_HW_DISK_ENCRYPT_PERF |
| 200 | -void get_blkdev_start_sector(int fd, unsigned long* st_sec); |
| 201 | -#endif |
| 202 | #endif |
| 203 | diff --git a/cryptfs.cpp b/cryptfs.cpp |
Jackeagle | 57acbdb | 2019-05-06 19:59:01 +0200 | [diff] [blame^] | 204 | index 6352de5..bea9b2f 100644 |
Jackeagle | 1cba413 | 2018-12-19 17:08:24 +0100 | [diff] [blame] | 205 | --- a/cryptfs.cpp |
| 206 | +++ b/cryptfs.cpp |
Jackeagle | 57acbdb | 2019-05-06 19:59:01 +0200 | [diff] [blame^] | 207 | @@ -1816,9 +1816,6 @@ static void cryptfs_trigger_restart_min_framework() |
Jackeagle | 1cba413 | 2018-12-19 17:08:24 +0100 | [diff] [blame] | 208 | static int cryptfs_restart_internal(int restart_main) |
| 209 | { |
| 210 | char crypto_blkdev[MAXPATHLEN]; |
| 211 | -#ifdef CONFIG_HW_DISK_ENCRYPTION |
| 212 | - char blkdev[MAXPATHLEN]; |
| 213 | -#endif |
| 214 | int rc = -1; |
| 215 | static int restart_successful = 0; |
| 216 | |
Jackeagle | 57acbdb | 2019-05-06 19:59:01 +0200 | [diff] [blame^] | 217 | @@ -1866,24 +1863,6 @@ static int cryptfs_restart_internal(int restart_main) |
Jackeagle | 1cba413 | 2018-12-19 17:08:24 +0100 | [diff] [blame] | 218 | * the tmpfs filesystem, and mount the real one. |
| 219 | */ |
| 220 | |
| 221 | -#if defined(CONFIG_HW_DISK_ENCRYPTION) |
| 222 | -#if defined(CONFIG_HW_DISK_ENCRYPT_PERF) |
| 223 | - if (is_ice_enabled()) { |
| 224 | - fs_mgr_get_crypt_info(fstab_default, 0, blkdev, sizeof(blkdev)); |
| 225 | - if (set_ice_param(START_ENCDEC)) { |
| 226 | - SLOGE("Failed to set ICE data"); |
| 227 | - return -1; |
| 228 | - } |
| 229 | - } |
| 230 | -#else |
| 231 | - property_get("ro.crypto.fs_crypto_blkdev", blkdev, ""); |
| 232 | - if (strlen(blkdev) == 0) { |
| 233 | - SLOGE("fs_crypto_blkdev not set\n"); |
| 234 | - return -1; |
| 235 | - } |
| 236 | - if (!(rc = wait_and_unmount(DATA_MNT_POINT, true))) { |
| 237 | -#endif |
| 238 | -#else |
| 239 | property_get("ro.crypto.fs_crypto_blkdev", crypto_blkdev, ""); |
| 240 | if (strlen(crypto_blkdev) == 0) { |
| 241 | SLOGE("fs_crypto_blkdev not set\n"); |
Jackeagle | 57acbdb | 2019-05-06 19:59:01 +0200 | [diff] [blame^] | 242 | @@ -1891,7 +1870,6 @@ static int cryptfs_restart_internal(int restart_main) |
Jackeagle | 1cba413 | 2018-12-19 17:08:24 +0100 | [diff] [blame] | 243 | } |
| 244 | |
| 245 | if (! (rc = wait_and_unmount(DATA_MNT_POINT, true)) ) { |
| 246 | -#endif |
| 247 | /* If ro.crypto.readonly is set to 1, mount the decrypted |
| 248 | * filesystem readonly. This is used when /data is mounted by |
| 249 | * recovery mode. |
Jackeagle | 57acbdb | 2019-05-06 19:59:01 +0200 | [diff] [blame^] | 250 | @@ -1915,26 +1893,15 @@ static int cryptfs_restart_internal(int restart_main) |
Jackeagle | 1cba413 | 2018-12-19 17:08:24 +0100 | [diff] [blame] | 251 | SLOGE("Failed to setexeccon"); |
| 252 | return -1; |
| 253 | } |
| 254 | -#ifdef CONFIG_HW_DISK_ENCRYPTION |
| 255 | - while ((mount_rc = fs_mgr_do_mount(fstab_default, DATA_MNT_POINT, |
| 256 | - blkdev, 0)) |
| 257 | - != 0) { |
| 258 | -#else |
| 259 | while ((mount_rc = fs_mgr_do_mount(fstab_default, DATA_MNT_POINT, |
| 260 | crypto_blkdev, 0)) |
| 261 | != 0) { |
| 262 | -#endif |
| 263 | if (mount_rc == FS_MGR_DOMNT_BUSY) { |
| 264 | /* TODO: invoke something similar to |
| 265 | Process::killProcessWithOpenFiles(DATA_MNT_POINT, |
| 266 | retries > RETRY_MOUNT_ATTEMPT/2 ? 1 : 2 ) */ |
| 267 | -#ifdef CONFIG_HW_DISK_ENCRYPTION |
| 268 | - SLOGI("Failed to mount %s because it is busy - waiting", |
| 269 | - blkdev); |
| 270 | -#else |
| 271 | SLOGI("Failed to mount %s because it is busy - waiting", |
| 272 | crypto_blkdev); |
| 273 | -#endif |
| 274 | if (--retries) { |
| 275 | sleep(RETRY_MOUNT_DELAY_SECONDS); |
| 276 | } else { |
Jackeagle | 57acbdb | 2019-05-06 19:59:01 +0200 | [diff] [blame^] | 277 | @@ -1980,9 +1947,7 @@ static int cryptfs_restart_internal(int restart_main) |
Jackeagle | 1cba413 | 2018-12-19 17:08:24 +0100 | [diff] [blame] | 278 | |
| 279 | /* Give it a few moments to get started */ |
| 280 | sleep(1); |
| 281 | -#ifndef CONFIG_HW_DISK_ENCRYPT_PERF |
| 282 | } |
| 283 | -#endif |
| 284 | |
| 285 | if (rc == 0) { |
| 286 | restart_successful = 1; |
Jackeagle | 57acbdb | 2019-05-06 19:59:01 +0200 | [diff] [blame^] | 287 | @@ -2084,14 +2049,12 @@ static int test_mount_hw_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr, |
Jackeagle | 1cba413 | 2018-12-19 17:08:24 +0100 | [diff] [blame] | 288 | } |
| 289 | else { |
| 290 | if (is_ice_enabled()) { |
| 291 | -#ifndef CONFIG_HW_DISK_ENCRYPT_PERF |
| 292 | if (create_crypto_blk_dev(crypt_ftr, (unsigned char*)&key_index, |
| 293 | real_blkdev, crypto_blkdev, label, 0)) { |
| 294 | SLOGE("Error creating decrypted block device"); |
| 295 | rc = -1; |
| 296 | goto errout; |
| 297 | } |
| 298 | -#endif |
| 299 | } else { |
| 300 | if (create_crypto_blk_dev(crypt_ftr, decrypted_master_key, |
| 301 | real_blkdev, crypto_blkdev, label, 0)) { |
Jackeagle | 57acbdb | 2019-05-06 19:59:01 +0200 | [diff] [blame^] | 302 | @@ -2111,9 +2074,6 @@ static int test_mount_hw_encrypted_fs(struct crypt_mnt_ftr* crypt_ftr, |
Jackeagle | 1cba413 | 2018-12-19 17:08:24 +0100 | [diff] [blame] | 303 | |
| 304 | /* Save the name of the crypto block device |
| 305 | * so we can mount it when restarting the framework. */ |
| 306 | -#ifdef CONFIG_HW_DISK_ENCRYPT_PERF |
| 307 | - if (!is_ice_enabled()) |
| 308 | -#endif |
| 309 | property_set("ro.crypto.fs_crypto_blkdev", crypto_blkdev); |
| 310 | master_key_saved = 1; |
| 311 | } |
Jackeagle | 57acbdb | 2019-05-06 19:59:01 +0200 | [diff] [blame^] | 312 | @@ -2871,12 +2831,8 @@ int cryptfs_enable_internal(int crypt_type, const char* passwd, int no_ui) { |
Jackeagle | 1cba413 | 2018-12-19 17:08:24 +0100 | [diff] [blame] | 313 | decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0); |
| 314 | #ifdef CONFIG_HW_DISK_ENCRYPTION |
| 315 | if (is_hw_disk_encryption((char*)crypt_ftr.crypto_type_name) && is_ice_enabled()) |
| 316 | -#ifdef CONFIG_HW_DISK_ENCRYPT_PERF |
| 317 | - strlcpy(crypto_blkdev, real_blkdev, sizeof(crypto_blkdev)); |
| 318 | -#else |
| 319 | create_crypto_blk_dev(&crypt_ftr, (unsigned char*)&key_index, real_blkdev, crypto_blkdev, |
| 320 | CRYPTO_BLOCK_DEVICE, 0); |
| 321 | -#endif |
| 322 | else |
| 323 | create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev, crypto_blkdev, |
| 324 | CRYPTO_BLOCK_DEVICE, 0); |
Jackeagle | 57acbdb | 2019-05-06 19:59:01 +0200 | [diff] [blame^] | 325 | @@ -2889,12 +2845,6 @@ int cryptfs_enable_internal(int crypt_type, const char* passwd, int no_ui) { |
Jackeagle | 1cba413 | 2018-12-19 17:08:24 +0100 | [diff] [blame] | 326 | rc = 0; |
| 327 | if (previously_encrypted_upto) { |
| 328 | __le8 hash_first_block[SHA256_DIGEST_LENGTH]; |
| 329 | -#if defined(CONFIG_HW_DISK_ENCRYPTION) && defined(CONFIG_HW_DISK_ENCRYPT_PERF) |
| 330 | - if (set_ice_param(START_ENCDEC)) { |
| 331 | - SLOGE("Failed to set ICE data"); |
| 332 | - goto error_shutting_down; |
| 333 | - } |
| 334 | -#endif |
| 335 | rc = cryptfs_SHA256_fileblock(crypto_blkdev, hash_first_block); |
| 336 | |
| 337 | if (!rc && memcmp(hash_first_block, crypt_ftr.hash_first_block, |
Jackeagle | 57acbdb | 2019-05-06 19:59:01 +0200 | [diff] [blame^] | 338 | @@ -2904,23 +2854,11 @@ int cryptfs_enable_internal(int crypt_type, const char* passwd, int no_ui) { |
Jackeagle | 1cba413 | 2018-12-19 17:08:24 +0100 | [diff] [blame] | 339 | } |
| 340 | } |
| 341 | |
| 342 | -#if defined(CONFIG_HW_DISK_ENCRYPTION) && defined(CONFIG_HW_DISK_ENCRYPT_PERF) |
| 343 | - if (set_ice_param(START_ENC)) { |
| 344 | - SLOGE("Failed to set ICE data"); |
| 345 | - goto error_shutting_down; |
| 346 | - } |
| 347 | -#endif |
| 348 | if (!rc) { |
| 349 | rc = cryptfs_enable_all_volumes(&crypt_ftr, crypto_blkdev, real_blkdev, |
| 350 | previously_encrypted_upto); |
| 351 | } |
| 352 | |
| 353 | -#if defined(CONFIG_HW_DISK_ENCRYPTION) && defined(CONFIG_HW_DISK_ENCRYPT_PERF) |
| 354 | - if (set_ice_param(START_ENCDEC)) { |
| 355 | - SLOGE("Failed to set ICE data"); |
| 356 | - goto error_shutting_down; |
| 357 | - } |
| 358 | -#endif |
| 359 | /* Calculate checksum if we are not finished */ |
| 360 | if (!rc && crypt_ftr.encrypted_upto != crypt_ftr.fs_size) { |
| 361 | rc = cryptfs_SHA256_fileblock(crypto_blkdev, |
Jackeagle | 57acbdb | 2019-05-06 19:59:01 +0200 | [diff] [blame^] | 362 | @@ -2932,12 +2870,7 @@ int cryptfs_enable_internal(int crypt_type, const char* passwd, int no_ui) { |
Jackeagle | 1cba413 | 2018-12-19 17:08:24 +0100 | [diff] [blame] | 363 | } |
| 364 | |
| 365 | /* Undo the dm-crypt mapping whether we succeed or not */ |
| 366 | -#if defined(CONFIG_HW_DISK_ENCRYPTION) && defined(CONFIG_HW_DISK_ENCRYPT_PERF) |
| 367 | - if (!is_ice_enabled()) |
| 368 | - delete_crypto_blk_dev(CRYPTO_BLOCK_DEVICE); |
| 369 | -#else |
| 370 | delete_crypto_blk_dev(CRYPTO_BLOCK_DEVICE); |
| 371 | -#endif |
| 372 | |
| 373 | if (! rc) { |
| 374 | /* Success */ |
| 375 | -- |
Jackeagle | 57acbdb | 2019-05-06 19:59:01 +0200 | [diff] [blame^] | 376 | 2.21.0 |
Jackeagle | 1cba413 | 2018-12-19 17:08:24 +0100 | [diff] [blame] | 377 | |