1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <unistd.h> 21 #include <fcntl.h> 22 #include <ctype.h> 23 #include <sys/mount.h> 24 #include <sys/stat.h> 25 #include <errno.h> 26 #include <sys/types.h> 27 #include <sys/wait.h> 28 #include <libgen.h> 29 #include <time.h> 30 #include <sys/swap.h> 31 32 #include <linux/loop.h> 33 #include <private/android_filesystem_config.h> 34 #include <cutils/android_reboot.h> 35 #include <cutils/partition_utils.h> 36 #include <cutils/properties.h> 37 #include <logwrap/logwrap.h> 38 39 #include "mincrypt/rsa.h" 40 #include "mincrypt/sha.h" 41 #include "mincrypt/sha256.h" 42 43 #include "fs_mgr_priv.h" 44 #include "fs_mgr_priv_verity.h" 45 46 #define KEY_LOC_PROP "ro.crypto.keyfile.userdata" 47 #define KEY_IN_FOOTER "footer" 48 49 #define E2FSCK_BIN "/system/bin/e2fsck" 50 #define F2FS_FSCK_BIN "/system/bin/fsck.f2fs" 51 #define MKSWAP_BIN "/system/bin/mkswap" 52 53 #define FSCK_LOG_FILE "/dev/fscklogs/log" 54 55 #define ZRAM_CONF_DEV "/sys/block/zram0/disksize" 56 57 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) 58 59 /* 60 * gettime() - returns the time in seconds of the system's monotonic clock or 61 * zero on error. 62 */ 63 static time_t gettime(void) 64 { 65 struct timespec ts; 66 int ret; 67 68 ret = clock_gettime(CLOCK_MONOTONIC, &ts); 69 if (ret < 0) { 70 ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno)); 71 return 0; 72 } 73 74 return ts.tv_sec; 75 } 76 77 static int wait_for_file(const char *filename, int timeout) 78 { 79 struct stat info; 80 time_t timeout_time = gettime() + timeout; 81 int ret = -1; 82 83 while (gettime() < timeout_time && ((ret = stat(filename, &info)) < 0)) 84 usleep(10000); 85 86 return ret; 87 } 88 89 static void check_fs(char *blk_device, char *fs_type, char *target) 90 { 91 int status; 92 int ret; 93 long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID; 94 char *tmpmnt_opts = "nomblk_io_submit,errors=remount-ro"; 95 char *e2fsck_argv[] = { 96 E2FSCK_BIN, 97 "-y", 98 blk_device 99 }; 100 101 /* Check for the types of filesystems we know how to check */ 102 if (!strcmp(fs_type, "ext2") || !strcmp(fs_type, "ext3") || !strcmp(fs_type, "ext4")) { 103 /* 104 * First try to mount and unmount the filesystem. We do this because 105 * the kernel is more efficient than e2fsck in running the journal and 106 * processing orphaned inodes, and on at least one device with a 107 * performance issue in the emmc firmware, it can take e2fsck 2.5 minutes 108 * to do what the kernel does in about a second. 109 * 110 * After mounting and unmounting the filesystem, run e2fsck, and if an 111 * error is recorded in the filesystem superblock, e2fsck will do a full 112 * check. Otherwise, it does nothing. If the kernel cannot mount the 113 * filesytsem due to an error, e2fsck is still run to do a full check 114 * fix the filesystem. 115 */ 116 ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts); 117 INFO("%s(): mount(%s,%s,%s)=%d\n", __func__, blk_device, target, fs_type, ret); 118 if (!ret) { 119 umount(target); 120 } 121 122 /* 123 * Some system images do not have e2fsck for licensing reasons 124 * (e.g. recent SDK system images). Detect these and skip the check. 125 */ 126 if (access(E2FSCK_BIN, X_OK)) { 127 INFO("Not running %s on %s (executable not in system image)\n", 128 E2FSCK_BIN, blk_device); 129 } else { 130 INFO("Running %s on %s\n", E2FSCK_BIN, blk_device); 131 132 ret = android_fork_execvp_ext(ARRAY_SIZE(e2fsck_argv), e2fsck_argv, 133 &status, true, LOG_KLOG | LOG_FILE, 134 true, FSCK_LOG_FILE); 135 136 if (ret < 0) { 137 /* No need to check for error in fork, we can't really handle it now */ 138 ERROR("Failed trying to run %s\n", E2FSCK_BIN); 139 } 140 } 141 } else if (!strcmp(fs_type, "f2fs")) { 142 char *f2fs_fsck_argv[] = { 143 F2FS_FSCK_BIN, 144 "-f", 145 blk_device 146 }; 147 INFO("Running %s -f %s\n", F2FS_FSCK_BIN, blk_device); 148 149 ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, 150 &status, true, LOG_KLOG | LOG_FILE, 151 true, FSCK_LOG_FILE); 152 if (ret < 0) { 153 /* No need to check for error in fork, we can't really handle it now */ 154 ERROR("Failed trying to run %s\n", F2FS_FSCK_BIN); 155 } 156 } 157 158 return; 159 } 160 161 static void remove_trailing_slashes(char *n) 162 { 163 int len; 164 165 len = strlen(n) - 1; 166 while ((*(n + len) == '/') && len) { 167 *(n + len) = '\0'; 168 len--; 169 } 170 } 171 172 /* 173 * Mark the given block device as read-only, using the BLKROSET ioctl. 174 * Return 0 on success, and -1 on error. 175 */ 176 static void fs_set_blk_ro(const char *blockdev) 177 { 178 int fd; 179 int ON = 1; 180 181 fd = open(blockdev, O_RDONLY); 182 if (fd < 0) { 183 // should never happen 184 return; 185 } 186 187 ioctl(fd, BLKROSET, &ON); 188 close(fd); 189 } 190 191 /* 192 * __mount(): wrapper around the mount() system call which also 193 * sets the underlying block device to read-only if the mount is read-only. 194 * See "man 2 mount" for return values. 195 */ 196 static int __mount(const char *source, const char *target, const struct fstab_rec *rec) 197 { 198 unsigned long mountflags = rec->flags; 199 int ret; 200 int save_errno; 201 202 /* We need this because sometimes we have legacy symlinks 203 * that are lingering around and need cleaning up. 204 */ 205 struct stat info; 206 if (!lstat(target, &info)) 207 if ((info.st_mode & S_IFMT) == S_IFLNK) 208 unlink(target); 209 mkdir(target, 0755); 210 ret = mount(source, target, rec->fs_type, mountflags, rec->fs_options); 211 save_errno = errno; 212 INFO("%s(source=%s,target=%s,type=%s)=%d\n", __func__, source, target, rec->fs_type, ret); 213 if ((ret == 0) && (mountflags & MS_RDONLY) != 0) { 214 fs_set_blk_ro(source); 215 } 216 errno = save_errno; 217 return ret; 218 } 219 220 static int fs_match(char *in1, char *in2) 221 { 222 char *n1; 223 char *n2; 224 int ret; 225 226 n1 = strdup(in1); 227 n2 = strdup(in2); 228 229 remove_trailing_slashes(n1); 230 remove_trailing_slashes(n2); 231 232 ret = !strcmp(n1, n2); 233 234 free(n1); 235 free(n2); 236 237 return ret; 238 } 239 240 static int device_is_debuggable() { 241 int ret = -1; 242 char value[PROP_VALUE_MAX]; 243 ret = __system_property_get("ro.debuggable", value); 244 if (ret < 0) 245 return ret; 246 return strcmp(value, "1") ? 0 : 1; 247 } 248 249 static int device_is_secure() { 250 int ret = -1; 251 char value[PROP_VALUE_MAX]; 252 ret = __system_property_get("ro.secure", value); 253 /* If error, we want to fail secure */ 254 if (ret < 0) 255 return 1; 256 return strcmp(value, "0") ? 1 : 0; 257 } 258 259 static int device_is_force_encrypted() { 260 int ret = -1; 261 char value[PROP_VALUE_MAX]; 262 ret = __system_property_get("ro.vold.forceencryption", value); 263 if (ret < 0) 264 return 0; 265 return strcmp(value, "1") ? 0 : 1; 266 } 267 268 /* 269 * Tries to mount any of the consecutive fstab entries that match 270 * the mountpoint of the one given by fstab->recs[start_idx]. 271 * 272 * end_idx: On return, will be the last rec that was looked at. 273 * attempted_idx: On return, will indicate which fstab rec 274 * succeeded. In case of failure, it will be the start_idx. 275 * Returns 276 * -1 on failure with errno set to match the 1st mount failure. 277 * 0 on success. 278 */ 279 static int mount_with_alternatives(struct fstab *fstab, int start_idx, int *end_idx, int *attempted_idx) 280 { 281 int i; 282 int mount_errno = 0; 283 int mounted = 0; 284 285 if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) { 286 errno = EINVAL; 287 if (end_idx) *end_idx = start_idx; 288 if (attempted_idx) *end_idx = start_idx; 289 return -1; 290 } 291 292 /* Hunt down an fstab entry for the same mount point that might succeed */ 293 for (i = start_idx; 294 /* We required that fstab entries for the same mountpoint be consecutive */ 295 i < fstab->num_entries && !strcmp(fstab->recs[start_idx].mount_point, fstab->recs[i].mount_point); 296 i++) { 297 /* 298 * Don't try to mount/encrypt the same mount point again. 299 * Deal with alternate entries for the same point which are required to be all following 300 * each other. 301 */ 302 if (mounted) { 303 ERROR("%s(): skipping fstab dup mountpoint=%s rec[%d].fs_type=%s already mounted as %s.\n", __func__, 304 fstab->recs[i].mount_point, i, fstab->recs[i].fs_type, fstab->recs[*attempted_idx].fs_type); 305 continue; 306 } 307 308 if (fstab->recs[i].fs_mgr_flags & MF_CHECK) { 309 check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type, 310 fstab->recs[i].mount_point); 311 } 312 if (!__mount(fstab->recs[i].blk_device, fstab->recs[i].mount_point, &fstab->recs[i])) { 313 *attempted_idx = i; 314 mounted = 1; 315 if (i != start_idx) { 316 ERROR("%s(): Mounted %s on %s with fs_type=%s instead of %s\n", __func__, 317 fstab->recs[i].blk_device, fstab->recs[i].mount_point, fstab->recs[i].fs_type, 318 fstab->recs[start_idx].fs_type); 319 } 320 } else { 321 /* back up errno for crypto decisions */ 322 mount_errno = errno; 323 } 324 } 325 326 /* Adjust i for the case where it was still withing the recs[] */ 327 if (i < fstab->num_entries) --i; 328 329 *end_idx = i; 330 if (!mounted) { 331 *attempted_idx = start_idx; 332 errno = mount_errno; 333 return -1; 334 } 335 return 0; 336 } 337 338 /* When multiple fstab records share the same mount_point, it will 339 * try to mount each one in turn, and ignore any duplicates after a 340 * first successful mount. 341 * Returns -1 on error, and FS_MGR_MNTALL_* otherwise. 342 */ 343 int fs_mgr_mount_all(struct fstab *fstab) 344 { 345 int i = 0; 346 int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTED; 347 int error_count = 0; 348 int mret = -1; 349 int mount_errno = 0; 350 int attempted_idx = -1; 351 352 if (!fstab) { 353 return -1; 354 } 355 356 for (i = 0; i < fstab->num_entries; i++) { 357 /* Don't mount entries that are managed by vold */ 358 if (fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) { 359 continue; 360 } 361 362 /* Skip swap and raw partition entries such as boot, recovery, etc */ 363 if (!strcmp(fstab->recs[i].fs_type, "swap") || 364 !strcmp(fstab->recs[i].fs_type, "emmc") || 365 !strcmp(fstab->recs[i].fs_type, "mtd")) { 366 continue; 367 } 368 369 if (fstab->recs[i].fs_mgr_flags & MF_WAIT) { 370 wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT); 371 } 372 373 if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) { 374 int rc = fs_mgr_setup_verity(&fstab->recs[i]); 375 if (device_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) { 376 INFO("Verity disabled"); 377 } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) { 378 ERROR("Could not set up verified partition, skipping!\n"); 379 continue; 380 } 381 } 382 int last_idx_inspected; 383 mret = mount_with_alternatives(fstab, i, &last_idx_inspected, &attempted_idx); 384 i = last_idx_inspected; 385 mount_errno = errno; 386 387 /* Deal with encryptability. */ 388 if (!mret) { 389 /* If this is encryptable, need to trigger encryption */ 390 if ( (fstab->recs[attempted_idx].fs_mgr_flags & MF_FORCECRYPT) 391 || (device_is_force_encrypted() 392 && fs_mgr_is_encryptable(&fstab->recs[attempted_idx]))) { 393 if (umount(fstab->recs[attempted_idx].mount_point) == 0) { 394 if (encryptable == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) { 395 ERROR("Will try to encrypt %s %s\n", fstab->recs[attempted_idx].mount_point, 396 fstab->recs[attempted_idx].fs_type); 397 encryptable = FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION; 398 } else { 399 ERROR("Only one encryptable/encrypted partition supported\n"); 400 encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED; 401 } 402 } else { 403 INFO("Could not umount %s - allow continue unencrypted\n", 404 fstab->recs[attempted_idx].mount_point); 405 continue; 406 } 407 } 408 /* Success! Go get the next one */ 409 continue; 410 } 411 412 /* mount(2) returned an error, check if it's encryptable and deal with it */ 413 if (mret && mount_errno != EBUSY && mount_errno != EACCES && 414 fs_mgr_is_encryptable(&fstab->recs[attempted_idx])) { 415 if(partition_wiped(fstab->recs[attempted_idx].blk_device)) { 416 ERROR("%s(): %s is wiped and %s %s is encryptable. Suggest recovery...\n", __func__, 417 fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, 418 fstab->recs[attempted_idx].fs_type); 419 encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY; 420 continue; 421 } else { 422 /* Need to mount a tmpfs at this mountpoint for now, and set 423 * properties that vold will query later for decrypting 424 */ 425 ERROR("%s(): possibly an encryptable blkdev %s for mount %s type %s )\n", __func__, 426 fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, 427 fstab->recs[attempted_idx].fs_type); 428 if (fs_mgr_do_tmpfs_mount(fstab->recs[attempted_idx].mount_point) < 0) { 429 ++error_count; 430 continue; 431 } 432 } 433 encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED; 434 } else { 435 ERROR("Failed to mount an un-encryptable or wiped partition on" 436 "%s at %s options: %s error: %s\n", 437 fstab->recs[attempted_idx].blk_device, fstab->recs[attempted_idx].mount_point, 438 fstab->recs[attempted_idx].fs_options, strerror(mount_errno)); 439 ++error_count; 440 continue; 441 } 442 } 443 444 if (error_count) { 445 return -1; 446 } else { 447 return encryptable; 448 } 449 } 450 451 /* If tmp_mount_point is non-null, mount the filesystem there. This is for the 452 * tmp mount we do to check the user password 453 * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one 454 * in turn, and stop on 1st success, or no more match. 455 */ 456 int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device, 457 char *tmp_mount_point) 458 { 459 int i = 0; 460 int ret = FS_MGR_DOMNT_FAILED; 461 int mount_errors = 0; 462 int first_mount_errno = 0; 463 char *m; 464 465 if (!fstab) { 466 return ret; 467 } 468 469 for (i = 0; i < fstab->num_entries; i++) { 470 if (!fs_match(fstab->recs[i].mount_point, n_name)) { 471 continue; 472 } 473 474 /* We found our match */ 475 /* If this swap or a raw partition, report an error */ 476 if (!strcmp(fstab->recs[i].fs_type, "swap") || 477 !strcmp(fstab->recs[i].fs_type, "emmc") || 478 !strcmp(fstab->recs[i].fs_type, "mtd")) { 479 ERROR("Cannot mount filesystem of type %s on %s\n", 480 fstab->recs[i].fs_type, n_blk_device); 481 goto out; 482 } 483 484 /* First check the filesystem if requested */ 485 if (fstab->recs[i].fs_mgr_flags & MF_WAIT) { 486 wait_for_file(n_blk_device, WAIT_TIMEOUT); 487 } 488 489 if (fstab->recs[i].fs_mgr_flags & MF_CHECK) { 490 check_fs(n_blk_device, fstab->recs[i].fs_type, 491 fstab->recs[i].mount_point); 492 } 493 494 if ((fstab->recs[i].fs_mgr_flags & MF_VERIFY) && device_is_secure()) { 495 int rc = fs_mgr_setup_verity(&fstab->recs[i]); 496 if (device_is_debuggable() && rc == FS_MGR_SETUP_VERITY_DISABLED) { 497 INFO("Verity disabled"); 498 } else if (rc != FS_MGR_SETUP_VERITY_SUCCESS) { 499 ERROR("Could not set up verified partition, skipping!\n"); 500 continue; 501 } 502 } 503 504 /* Now mount it where requested */ 505 if (tmp_mount_point) { 506 m = tmp_mount_point; 507 } else { 508 m = fstab->recs[i].mount_point; 509 } 510 if (__mount(n_blk_device, m, &fstab->recs[i])) { 511 if (!first_mount_errno) first_mount_errno = errno; 512 mount_errors++; 513 continue; 514 } else { 515 ret = 0; 516 goto out; 517 } 518 } 519 if (mount_errors) { 520 ERROR("Cannot mount filesystem on %s at %s. error: %s\n", 521 n_blk_device, m, strerror(first_mount_errno)); 522 if (first_mount_errno == EBUSY) { 523 ret = FS_MGR_DOMNT_BUSY; 524 } else { 525 ret = FS_MGR_DOMNT_FAILED; 526 } 527 } else { 528 /* We didn't find a match, say so and return an error */ 529 ERROR("Cannot find mount point %s in fstab\n", fstab->recs[i].mount_point); 530 } 531 532 out: 533 return ret; 534 } 535 536 /* 537 * mount a tmpfs filesystem at the given point. 538 * return 0 on success, non-zero on failure. 539 */ 540 int fs_mgr_do_tmpfs_mount(char *n_name) 541 { 542 int ret; 543 544 ret = mount("tmpfs", n_name, "tmpfs", 545 MS_NOATIME | MS_NOSUID | MS_NODEV, CRYPTO_TMPFS_OPTIONS); 546 if (ret < 0) { 547 ERROR("Cannot mount tmpfs filesystem at %s\n", n_name); 548 return -1; 549 } 550 551 /* Success */ 552 return 0; 553 } 554 555 int fs_mgr_unmount_all(struct fstab *fstab) 556 { 557 int i = 0; 558 int ret = 0; 559 560 if (!fstab) { 561 return -1; 562 } 563 564 while (fstab->recs[i].blk_device) { 565 if (umount(fstab->recs[i].mount_point)) { 566 ERROR("Cannot unmount filesystem at %s\n", fstab->recs[i].mount_point); 567 ret = -1; 568 } 569 i++; 570 } 571 572 return ret; 573 } 574 575 /* This must be called after mount_all, because the mkswap command needs to be 576 * available. 577 */ 578 int fs_mgr_swapon_all(struct fstab *fstab) 579 { 580 int i = 0; 581 int flags = 0; 582 int err = 0; 583 int ret = 0; 584 int status; 585 char *mkswap_argv[2] = { 586 MKSWAP_BIN, 587 NULL 588 }; 589 590 if (!fstab) { 591 return -1; 592 } 593 594 for (i = 0; i < fstab->num_entries; i++) { 595 /* Skip non-swap entries */ 596 if (strcmp(fstab->recs[i].fs_type, "swap")) { 597 continue; 598 } 599 600 if (fstab->recs[i].zram_size > 0) { 601 /* A zram_size was specified, so we need to configure the 602 * device. There is no point in having multiple zram devices 603 * on a system (all the memory comes from the same pool) so 604 * we can assume the device number is 0. 605 */ 606 FILE *zram_fp; 607 608 zram_fp = fopen(ZRAM_CONF_DEV, "r+"); 609 if (zram_fp == NULL) { 610 ERROR("Unable to open zram conf device %s\n", ZRAM_CONF_DEV); 611 ret = -1; 612 continue; 613 } 614 fprintf(zram_fp, "%d\n", fstab->recs[i].zram_size); 615 fclose(zram_fp); 616 } 617 618 if (fstab->recs[i].fs_mgr_flags & MF_WAIT) { 619 wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT); 620 } 621 622 /* Initialize the swap area */ 623 mkswap_argv[1] = fstab->recs[i].blk_device; 624 err = android_fork_execvp_ext(ARRAY_SIZE(mkswap_argv), mkswap_argv, 625 &status, true, LOG_KLOG, false, NULL); 626 if (err) { 627 ERROR("mkswap failed for %s\n", fstab->recs[i].blk_device); 628 ret = -1; 629 continue; 630 } 631 632 /* If -1, then no priority was specified in fstab, so don't set 633 * SWAP_FLAG_PREFER or encode the priority */ 634 if (fstab->recs[i].swap_prio >= 0) { 635 flags = (fstab->recs[i].swap_prio << SWAP_FLAG_PRIO_SHIFT) & 636 SWAP_FLAG_PRIO_MASK; 637 flags |= SWAP_FLAG_PREFER; 638 } else { 639 flags = 0; 640 } 641 err = swapon(fstab->recs[i].blk_device, flags); 642 if (err) { 643 ERROR("swapon failed for %s\n", fstab->recs[i].blk_device); 644 ret = -1; 645 } 646 } 647 648 return ret; 649 } 650 651 /* 652 * key_loc must be at least PROPERTY_VALUE_MAX bytes long 653 * 654 * real_blk_device must be at least PROPERTY_VALUE_MAX bytes long 655 */ 656 int fs_mgr_get_crypt_info(struct fstab *fstab, char *key_loc, char *real_blk_device, int size) 657 { 658 int i = 0; 659 660 if (!fstab) { 661 return -1; 662 } 663 /* Initialize return values to null strings */ 664 if (key_loc) { 665 *key_loc = '\0'; 666 } 667 if (real_blk_device) { 668 *real_blk_device = '\0'; 669 } 670 671 /* Look for the encryptable partition to find the data */ 672 for (i = 0; i < fstab->num_entries; i++) { 673 /* Don't deal with vold managed enryptable partitions here */ 674 if (fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) { 675 continue; 676 } 677 if (!(fstab->recs[i].fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT))) { 678 continue; 679 } 680 681 /* We found a match */ 682 if (key_loc) { 683 strlcpy(key_loc, fstab->recs[i].key_loc, size); 684 } 685 if (real_blk_device) { 686 strlcpy(real_blk_device, fstab->recs[i].blk_device, size); 687 } 688 break; 689 } 690 691 return 0; 692 } 693