fstab_mount.c 16.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
C
slot  
chengjinsong 已提交
24
#include <linux/limits.h>
X
xionglei6 已提交
25
#include "beget_ext.h"
26 27
#include "fs_manager/fs_manager.h"
#include "init_utils.h"
C
slot  
chengjinsong 已提交
28
#include "param/init_param.h"
29 30
#include "securec.h"

S
sun_fan 已提交
31 32 33 34 35 36
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif

37
#define FS_MANAGER_BUFFER_SIZE 512
S
sun_fan 已提交
38
#define BLOCK_SIZE_BUFFER (64)
39
#define RESIZE_BUFFER_SIZE 1024
C
slot  
chengjinsong 已提交
40 41
const off_t PARTITION_ACTIVE_SLOT_OFFSET = 1024;
const off_t PARTITION_ACTIVE_SLOT_SIZE = 4;
S
sun_fan 已提交
42

43 44 45 46
bool IsSupportedFilesystem(const char *fsType)
{
    bool supported = false;
    if (fsType != NULL) {
C
codex  
chengjinsong 已提交
47 48
        static const char *supportedFilesystem[] = {"ext4", "f2fs", NULL};
        int index = 0;
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
        while (supportedFilesystem[index] != NULL) {
            if (strcmp(supportedFilesystem[index++], fsType) == 0) {
                supported = true;
                break;
            }
        }
    }
    return supported;
}

static int ExecCommand(int argc, char **argv)
{
    if (argc == 0 || argv == NULL || argv[0] == NULL) {
        return -1;
    }
C
chengjinsong 已提交
64
    BEGET_LOGI("Execute %s begin", argv[0]);
65 66
    pid_t pid = fork();
    if (pid < 0) {
X
xionglei6 已提交
67
        BEGET_LOGE("Fork new process to format failed: %d", errno);
68 69 70 71 72 73 74 75 76
        return -1;
    }
    if (pid == 0) {
        execv(argv[0], argv);
        exit(-1);
    }
    int status;
    waitpid(pid, &status, 0);
    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
X
xionglei6 已提交
77
        BEGET_LOGE("Command %s failed with status %d", argv[0], WEXITSTATUS(status));
78
    }
C
chengjinsong 已提交
79
    BEGET_LOGI("Execute %s end", argv[0]);
80 81 82 83 84 85 86 87 88 89
    return WEXITSTATUS(status);
}

int DoFormat(const char *devPath, const char *fsType)
{
    if (devPath == NULL || fsType == NULL) {
        return -1;
    }

    if (!IsSupportedFilesystem(fsType)) {
X
xionglei6 已提交
90
        BEGET_LOGE("Do not support filesystem \" %s \"", fsType);
91 92 93 94
        return -1;
    }
    int ret = 0;
    if (strcmp(fsType, "ext4") == 0) {
C
codex  
chengjinsong 已提交
95
        char blockSizeBuffer[BLOCK_SIZE_BUFFER] = {0};
96 97
        const unsigned int blockSize = 4096;
        if (snprintf_s(blockSizeBuffer, BLOCK_SIZE_BUFFER, BLOCK_SIZE_BUFFER - 1, "%u", blockSize) == -1) {
X
xionglei6 已提交
98
            BEGET_LOGE("Failed to build block size buffer");
99 100 101 102 103 104 105 106 107
            return -1;
        }
        char *formatCmds[] = {
            "/bin/mke2fs", "-F", "-t", (char *)fsType, "-b", blockSizeBuffer, (char *)devPath, NULL
        };
        int argc = ARRAY_LENGTH(formatCmds);
        char **argv = (char **)formatCmds;
        ret = ExecCommand(argc, argv);
    } else if (strcmp(fsType, "f2fs") == 0) {
108
#ifdef __MUSL__
109
        char *formatCmds[] = {
Z
zhangwendi 已提交
110
            "/bin/mkfs.f2fs", "-d1", "-O", "encrypt", "-O", "quota", "-O", "verity", (char *)devPath, NULL
111
        };
112 113
#else
        char *formatCmds[] = {
Z
zhangwendi 已提交
114
            "/bin/make_f2fs", "-d1", "-O", "encrypt", "-O", "quota", "-O", "verity", (char *)devPath, NULL
115 116
        };
#endif
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
        int argc = ARRAY_LENGTH(formatCmds);
        char **argv = (char **)formatCmds;
        ret = ExecCommand(argc, argv);
    }
    return ret;
}

MountStatus GetMountStatusForMountPoint(const char *mp)
{
    if (mp == NULL) {
        return MOUNT_ERROR;
    }
    char buffer[FS_MANAGER_BUFFER_SIZE] = {0};
    const int expectedItems = 6;
    int count = 0;
    char **mountItems = NULL;
    MountStatus status = MOUNT_ERROR;
    bool found = false;

    FILE *fp = fopen("/proc/mounts", "r");
    if (fp == NULL) {
        return status;
    }
    while (fgets(buffer, sizeof(buffer) - 1, fp) != NULL) {
X
xionglei6 已提交
141
        size_t n = strlen(buffer);
142 143 144 145 146 147 148 149 150 151 152 153 154 155
        if (buffer[n - 1] == '\n') {
            buffer[n - 1] = '\0';
        }
        mountItems = SplitStringExt(buffer, " ", &count, expectedItems);
        if (mountItems != NULL && count == expectedItems) {
            // Second item in /proc/mounts is mount point
            if (strcmp(mountItems[1], mp) == 0) {
                FreeStringVector(mountItems, count);
                found = true;
                break;
            }
            FreeStringVector(mountItems, count);
        }
    }
X
xionglei6 已提交
156
    if (found) {
157 158 159 160
        status = MOUNT_MOUNTED;
    } else if (feof(fp) > 0) {
        status = MOUNT_UMOUNTED;
    }
S
sun_fan 已提交
161
    (void)fclose(fp);
162 163 164 165
    fp = NULL;
    return status;
}

166 167 168 169
static int DoResizeF2fs(const char* device, const unsigned long long size)
{
    char *file = "/system/bin/resize.f2fs";
    if (access(file, F_OK) != 0) {
X
xionglei6 已提交
170
        BEGET_LOGE("resize.f2fs is not exists.");
171 172 173 174
        return -1;
    }

    int ret = 0;
C
codex  
chengjinsong 已提交
175
    if (size == 0) {
176
        char *cmd[] = {
177
            file, (char *)device, NULL
178 179 180 181 182
        };
        int argc = ARRAY_LENGTH(cmd);
        char **argv = (char **)cmd;
        ret = ExecCommand(argc, argv);
    } else {
X
xionglei6 已提交
183 184
        unsigned long long realSize = size *
            ((unsigned long long)RESIZE_BUFFER_SIZE * RESIZE_BUFFER_SIZE / FS_MANAGER_BUFFER_SIZE);
185
        char sizeStr[RESIZE_BUFFER_SIZE] = {0};
weiying440's avatar
weiying440 已提交
186 187 188 189
        int len = sprintf_s(sizeStr, RESIZE_BUFFER_SIZE, "%llu", realSize);
        if (len <= 0) {
            BEGET_LOGE("Write buffer size failed.");
        }
190
        char *cmd[] = {
191
            file, "-t", sizeStr, (char *)device, NULL
192 193 194 195 196 197 198 199 200 201 202 203
        };
        int argc = ARRAY_LENGTH(cmd);
        char **argv = (char **)cmd;
        ret = ExecCommand(argc, argv);
    }
    return ret;
}

static int DoFsckF2fs(const char* device)
{
    char *file = "/system/bin/fsck.f2fs";
    if (access(file, F_OK) != 0) {
X
xionglei6 已提交
204
        BEGET_LOGE("fsck.f2fs is not exists.");
205 206 207 208 209 210 211 212
        return -1;
    }

    char *cmd[] = {
        file, "-a", (char *)device, NULL
    };
    int argc = ARRAY_LENGTH(cmd);
    char **argv = (char **)cmd;
X
xlfeng 已提交
213
    return ExecCommand(argc, argv);
214 215 216 217 218 219
}

static int DoResizeExt(const char* device, const unsigned long long size)
{
    char *file = "/system/bin/resize2fs";
    if (access(file, F_OK) != 0) {
X
xionglei6 已提交
220
        BEGET_LOGE("resize2fs is not exists.");
221 222 223 224
        return -1;
    }

    int ret = 0;
C
codex  
chengjinsong 已提交
225
    if (size == 0) {
226 227 228 229 230 231 232 233
        char *cmd[] = {
            file, "-f", (char *)device, NULL
        };
        int argc = ARRAY_LENGTH(cmd);
        char **argv = (char **)cmd;
        ret = ExecCommand(argc, argv);
    } else {
        char sizeStr[RESIZE_BUFFER_SIZE] = {0};
weiying440's avatar
weiying440 已提交
234 235 236 237
        int len = sprintf_s(sizeStr, RESIZE_BUFFER_SIZE, "%lluM", size);
        if (len <= 0) {
            BEGET_LOGE("Write buffer size failed.");
        }
238 239 240 241 242 243 244 245 246 247 248 249 250 251
        char *cmd[] = {
            file, "-f", (char *)device, sizeStr, NULL
        };
        int argc = ARRAY_LENGTH(cmd);
        char **argv = (char **)cmd;
        ret = ExecCommand(argc, argv);
    }
    return ret;
}

static int DoFsckExt(const char* device)
{
    char *file = "/system/bin/e2fsck";
    if (access(file, F_OK) != 0) {
X
xionglei6 已提交
252
        BEGET_LOGE("e2fsck is not exists.");
253 254 255 256 257 258 259 260 261 262 263
        return -1;
    }

    char *cmd[] = {
        file, "-y", (char *)device, NULL
    };
    int argc = ARRAY_LENGTH(cmd);
    char **argv = (char **)cmd;
    return ExecCommand(argc, argv);
}

264 265 266 267 268 269 270
static int Mount(const char *source, const char *target, const char *fsType,
    unsigned long flags, const char *data)
{
    struct stat st = {};
    int rc = -1;

    if (source == NULL || target == NULL || fsType == NULL) {
W
Wen liumin 已提交
271
        BEGET_LOGE("Invalid argument for mount.");
272 273 274
        return -1;
    }
    if (stat(target, &st) != 0 && errno != ENOENT) {
X
xionglei6 已提交
275
        BEGET_LOGE("Cannot get stat of \" %s \", err = %d", target, errno);
276 277 278 279 280 281 282
        return -1;
    }
    if ((st.st_mode & S_IFMT) == S_IFLNK) { // link, delete it.
        unlink(target);
    }
    if (mkdir(target, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
        if (errno != EEXIST) {
X
xionglei6 已提交
283
            BEGET_LOGE("Failed to create dir \" %s \", err = %d", target, errno);
284 285 286 287
            return -1;
        }
    }
    errno = 0;
C
chengjinsong 已提交
288 289
    if ((rc = mount(source, target, fsType, flags, data)) != 0) {
        BEGET_WARNING_CHECK(errno != EBUSY, rc = 0, "Mount %s to %s busy, ignore", source, target);
290 291 292 293
    }
    return rc;
}

C
slot  
chengjinsong 已提交
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
static int GetSlotInfoFromParameter(const char *slotInfoName)
{
    char name[PARAM_NAME_LEN_MAX] = {0};
    BEGET_ERROR_CHECK(sprintf_s(name, sizeof(name), "ohos.boot.%s", slotInfoName) > 0,
        return -1, "Failed to format slot parameter name");
    char value[PARAM_VALUE_LEN_MAX] = {0};
    uint32_t valueLen = PARAM_VALUE_LEN_MAX;
    return SystemGetParameter(name, value, &valueLen) == 0 ? atoi(value) : -1;
}

static int GetSlotInfoFromCmdLine(const char *slotInfoName)
{
    char value[MAX_BUFFER_LEN] = {0};
    char *buffer = ReadFileData(BOOT_CMD_LINE);
    BEGET_ERROR_CHECK(buffer != NULL, return -1, "Failed to read cmdline");
Y
yichengzhao 已提交
309
    BEGET_INFO_CHECK(GetProcCmdlineValue(slotInfoName, buffer, value, MAX_BUFFER_LEN) == 0,
C
slot  
chengjinsong 已提交
310 311 312 313 314 315
        free(buffer); buffer = NULL; return -1, "Failed to get %s value from cmdline", slotInfoName);
    free(buffer);
    buffer = NULL;
    return atoi(value);
}

C
slot  
chengjinsong 已提交
316
static int GetSlotInfoFromBootctrl(off_t offset, off_t size)
C
slot  
chengjinsong 已提交
317
{
C
slot  
chengjinsong 已提交
318 319 320 321 322
    char bootctrlDev[MAX_BUFFER_LEN] = {0};
    BEGET_ERROR_CHECK(GetBlockDevicePath("/bootctrl", bootctrlDev, MAX_BUFFER_LEN) == 0,
        return -1, "Failed to get bootctrl device");
    char *realPath = GetRealPath(bootctrlDev);
    BEGET_ERROR_CHECK(realPath != NULL, return -1, "Failed to get bootctrl device real path");
C
codex  
chengjinsong 已提交
323 324
    int fd = open(realPath, O_RDWR | O_CLOEXEC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    free(realPath);
C
slot  
chengjinsong 已提交
325
    BEGET_ERROR_CHECK(fd >= 0, return -1, "Failed to open bootctrl device, errno %d", errno);
C
slot  
chengjinsong 已提交
326
    BEGET_ERROR_CHECK(lseek(fd, offset, SEEK_SET) >= 0, close(fd); return -1,
C
slot  
chengjinsong 已提交
327
        "Failed to lseek bootctrl device fd, errno %d", errno);
C
slot  
chengjinsong 已提交
328
    int slotInfo = 0;
C
cheng_jinsong 已提交
329
    BEGET_INFO_CHECK(read(fd, &slotInfo, sizeof(slotInfo)) == size, close(fd); return -1,
C
slot  
chengjinsong 已提交
330
        "Failed to read current slot from bootctrl, errno %d", errno);
C
slot  
chengjinsong 已提交
331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
    close(fd);
    return slotInfo;
}

int GetBootSlots(void)
{
    int bootSlots = GetSlotInfoFromParameter("bootslots");
    BEGET_CHECK_RETURN_VALUE(bootSlots <= 0, bootSlots);
    BEGET_LOGI("No valid slot value found from parameter, try to get it from cmdline");
    return GetSlotInfoFromCmdLine("bootslots");
}

int GetCurrentSlot(void)
{
    // get current slot from parameter
    int currentSlot = GetSlotInfoFromParameter("currentslot");
    BEGET_CHECK_RETURN_VALUE(currentSlot <= 0, currentSlot);
    BEGET_LOGI("No valid slot value found from parameter, try to get it from cmdline");

    // get current slot from cmdline
    currentSlot = GetSlotInfoFromCmdLine("currentslot");
    BEGET_CHECK_RETURN_VALUE(currentSlot <= 0, currentSlot);
C
slot  
chengjinsong 已提交
353
    BEGET_LOGI("No valid slot value found from cmdline, try to get it from bootctrl");
C
slot  
chengjinsong 已提交
354

C
slot  
chengjinsong 已提交
355 356
    // get current slot from bootctrl
    return GetSlotInfoFromBootctrl(PARTITION_ACTIVE_SLOT_OFFSET, PARTITION_ACTIVE_SLOT_SIZE);
C
slot  
chengjinsong 已提交
357 358
}

359 360 361 362 363 364 365 366
int MountOneItem(FstabItem *item)
{
    if (item == NULL) {
        return -1;
    }
    unsigned long mountFlags;
    char fsSpecificData[FS_MANAGER_BUFFER_SIZE] = {0};

367 368
    mountFlags = GetMountFlags(item->mountOptions, fsSpecificData, sizeof(fsSpecificData),
        item->mountPoint);
369
    if (!IsSupportedFilesystem(item->fsType)) {
X
xionglei6 已提交
370
        BEGET_LOGE("Unsupported file system \" %s \"", item->fsType);
X
xionglei6 已提交
371
        return 0;
372 373
    }
    if (FM_MANAGER_WAIT_ENABLED(item->fsManagerFlags)) {
H
huangshan 已提交
374
        WaitForFile(item->deviceName, WAIT_MAX_SECOND);
375
    }
376 377 378 379

    if (strcmp(item->fsType, "f2fs") == 0 && strcmp(item->mountPoint, "/data") == 0) {
        int ret = DoResizeF2fs(item->deviceName, 0);
        if (ret != 0) {
X
xionglei6 已提交
380
            BEGET_LOGE("Failed to resize.f2fs dir %s , ret = %d", item->deviceName, ret);
381 382 383 384
        }

        ret = DoFsckF2fs(item->deviceName);
        if (ret != 0) {
X
xionglei6 已提交
385
            BEGET_LOGE("Failed to fsck.f2fs dir %s , ret = %d", item->deviceName, ret);
386 387 388 389
        }
    } else if (strcmp(item->fsType, "ext4") == 0 && strcmp(item->mountPoint, "/data") == 0) {
        int ret = DoResizeExt(item->deviceName, 0);
        if (ret != 0) {
X
xionglei6 已提交
390
            BEGET_LOGE("Failed to resize2fs dir %s , ret = %d", item->deviceName, ret);
391 392 393
        }
        ret = DoFsckExt(item->deviceName);
        if (ret != 0) {
X
xionglei6 已提交
394
            BEGET_LOGE("Failed to e2fsck dir %s , ret = %d", item->deviceName, ret);
395 396 397
        }
    }

398 399
    int rc = Mount(item->deviceName, item->mountPoint, item->fsType, mountFlags, fsSpecificData);
    if (rc != 0) {
C
nofail  
chengjinsong 已提交
400 401 402 403 404 405
        if (FM_MANAGER_NOFAIL_ENABLED(item->fsManagerFlags)) {
            BEGET_LOGE("Mount no fail device %s to %s failed, err = %d", item->deviceName, item->mountPoint, errno);
        } else {
            BEGET_LOGW("Mount %s to %s failed, err = %d. Ignore failure", item->deviceName, item->mountPoint, errno);
            rc = 0;
        }
406
    } else {
X
xionglei6 已提交
407
        BEGET_LOGI("Mount %s to %s successful", item->deviceName, item->mountPoint);
408 409 410 411
    }
    return rc;
}

C
slot  
chengjinsong 已提交
412 413 414 415 416 417 418 419 420 421 422 423 424 425
static void AdjustPartitionNameByPartitionSlot(FstabItem *item)
{
    BEGET_CHECK_ONLY_RETURN(strstr(item->deviceName, "/system") != NULL ||
        strstr(item->deviceName, "/vendor") != NULL);
    char buffer[MAX_BUFFER_LEN] = {0};
    int slot = GetCurrentSlot();
    BEGET_ERROR_CHECK(slot > 0 && slot <= MAX_SLOT, slot = 1, "slot value %d is invalid, set default value", slot);
    BEGET_ERROR_CHECK(sprintf_s(buffer, sizeof(buffer), "%s_%c", item->deviceName, 'a' + slot - 1) > 0,
        return, "Failed to format partition name suffix, use default partition name");
    free(item->deviceName);
    item->deviceName = strdup(buffer);
    BEGET_LOGI("partition name with slot suffix: %s", item->deviceName);
}

426
static int CheckRequiredAndMount(FstabItem *item, bool required)
427
{
S
sun_fan 已提交
428
    int rc = 0;
429
    if (item == NULL) {
S
sun_fan 已提交
430
        return -1;
431 432 433
    }
    if (required) { // Mount partition during first startup.
        if (FM_MANAGER_REQUIRED_ENABLED(item->fsManagerFlags)) {
C
slot  
chengjinsong 已提交
434 435 436
            int bootSlots = GetBootSlots();
            BEGET_INFO_CHECK(bootSlots <= 1, AdjustPartitionNameByPartitionSlot(item),
                "boot slots is %d, now adjust partition name according to current slot", bootSlots);
437 438 439 440 441 442 443 444 445 446
            rc = MountOneItem(item);
        }
    } else { // Mount partition during second startup.
        if (!FM_MANAGER_REQUIRED_ENABLED(item->fsManagerFlags)) {
            rc = MountOneItem(item);
        }
    }
    return rc;
}

X
xionglei6 已提交
447
int MountAllWithFstab(const Fstab *fstab, bool required)
448
{
X
xionglei6 已提交
449
    if (fstab == NULL) {
450 451 452 453 454 455 456
        return -1;
    }

    FstabItem *item = NULL;
    int rc = -1;
    for (item = fstab->head; item != NULL; item = item->next) {
        rc = CheckRequiredAndMount(item, required);
S
sun_fan 已提交
457 458 459
        if (required && (rc < 0)) { // Init fail to mount in the first stage and exit directly.
            break;
        }
460
    }
X
xionglei6 已提交
461 462 463 464 465 466 467 468 469 470
    return rc;
}

int MountAllWithFstabFile(const char *fstabFile, bool required)
{
    if (fstabFile == NULL || *fstabFile == '\0') {
        return -1;
    }
    Fstab *fstab = NULL;
    if ((fstab = ReadFstabFromFile(fstabFile, false)) == NULL) {
X
xionglei6 已提交
471
        BEGET_LOGE("[fs_manager][error] Read fstab file \" %s \" failed\n", fstabFile);
X
xionglei6 已提交
472 473 474 475
        return -1;
    }

    int rc = MountAllWithFstab(fstab, required);
476 477 478 479 480 481 482 483 484 485 486 487
    ReleaseFstab(fstab);
    fstab = NULL;
    return rc;
}

int UmountAllWithFstabFile(const char *fstabFile)
{
    if (fstabFile == NULL || *fstabFile == '\0') {
        return -1;
    }
    Fstab *fstab = NULL;
    if ((fstab = ReadFstabFromFile(fstabFile, false)) == NULL) {
X
xionglei6 已提交
488
        BEGET_LOGE("Read fstab file \" %s \" failed.", fstabFile);
S
sun_fan 已提交
489
        return -1;
490 491 492 493 494
    }

    FstabItem *item = NULL;
    int rc = -1;
    for (item = fstab->head; item != NULL; item = item->next) {
X
xionglei6 已提交
495
        BEGET_LOGI("Umount %s.", item->mountPoint);
496 497
        MountStatus status = GetMountStatusForMountPoint(item->mountPoint);
        if (status == MOUNT_ERROR) {
X
xionglei6 已提交
498
            BEGET_LOGW("Cannot get mount status of mount point \" %s \"", item->mountPoint);
499 500
            continue; // Cannot get mount status, just ignore it and try next one.
        } else if (status == MOUNT_UMOUNTED) {
X
xionglei6 已提交
501
            BEGET_LOGI("Mount point \" %s \" already unmounted. device path: %s, fs type: %s.",
502 503 504 505 506
                item->mountPoint, item->deviceName, item->fsType);
            continue;
        } else {
            rc = umount(item->mountPoint);
            if (rc == -1) {
X
xionglei6 已提交
507
                BEGET_LOGE("Umount %s failed, device path: %s, fs type: %s, err = %d.",
508 509
                    item->mountPoint, item->deviceName, item->fsType, errno);
            } else {
X
xionglei6 已提交
510
                BEGET_LOGE("Umount %s successfully.", item->mountPoint);
511 512 513 514 515 516 517 518 519 520 521
            }
        }
    }
    ReleaseFstab(fstab);
    fstab = NULL;
    return rc;
}
#ifdef __cplusplus
#if __cplusplus
}
#endif
X
xionglei6 已提交
522
#endif