Пример #1
0
Файл: dir.c Проект: antontest/c
/**
 * @brief get size of dir 
 *
 * @param pathname [in] path of dir
 *
 * @return size of dir, if succ; -1, if failed.
 */
int get_dir_size(const char *pathname)
{
    struct dirent *dir = NULL;
    struct stat st = {0};
    DIR *d = NULL;
    int total_size = 0;
    char buf[512] = {0};

    if (pathname == NULL) return -1;
    if (lstat(pathname, &st) < 0) return -1;
    if (!S_ISDIR(st.st_mode)) return -1;
    if ((d = opendir(pathname)) == NULL) return -1;

    while ((dir = readdir(d)) != NULL)
    {
        if (!strcmp(dir->d_name, ".") || !strcmp(dir->d_name, "..")) continue;
        
        sprintf(buf, "%s/%s", pathname, dir->d_name);
        if (lstat(buf, &st) >= 0) 
        {
            if (S_ISDIR(st.st_mode)) 
            {
                total_size += get_dir_size(dir->d_name);
            }
            else total_size += st.st_size;
        }
    }
    closedir(d);

    return total_size;
}
static double get_dir_size(const char *dirname,
                           char **worst_file,
                           double *worst_file_size)
{
    DIR *dp = opendir(dirname);
    if (!dp)
        return 0;

    /* "now" is used only if caller wants to know worst_file */
    time_t now = worst_file ? time(NULL) : 0;
    struct dirent *dent;
    struct stat stats;
    double size = 0;
    while ((dent = readdir(dp)) != NULL)
    {
        if (dot_or_dotdot(dent->d_name))
            continue;

        char *fullname = concat_path_file(dirname, dent->d_name);
        if (lstat(fullname, &stats) != 0)
        {
            free(fullname);
            continue;
        }

        if (S_ISDIR(stats.st_mode))
        {
            double sz = get_dir_size(fullname, worst_file, worst_file_size);
            size += sz;
        }
        else if (S_ISREG(stats.st_mode))
        {
            double sz = stats.st_size;
            size += sz;

            if (worst_file)
            {
                /* Calculate "weighted" size and age
                 * w = sz_kbytes * age_mins */
                sz /= 1024;
                long age = (now - stats.st_mtime) / 60;
                if (age > 0)
                    sz *= age;

                if (sz > *worst_file_size)
                {
                    *worst_file_size = sz;
                    free(*worst_file);
                    *worst_file = fullname;
                    fullname = NULL;
                }
            }
        }
        free(fullname);
    }
    closedir(dp);

    return size;
}
Пример #3
0
Файл: dir.c Проект: antontest/c
/**
 * @brief get size of file
 *
 * @param pathname [in] pathname of file
 *
 * @return size, if succ; -1, if failed.
 */
int get_file_size(const char *pathname)
{
    struct stat st = {0};

    if (pathname == NULL) return -1;
    if (lstat(pathname, &st) < 0) return -1;

    if (S_ISDIR(st.st_mode)) return get_dir_size(pathname);
    else return st.st_size;

    return -1;
}
Пример #4
0
ssize_t
get_dir_size(struct mic_info *mic, char *dirpath)
{
	struct dirent *tmp;
	DIR *dir;
	char path[PATH_MAX];
	struct stat data;
	size_t result = 0;

	if ((dir = opendir(dirpath)) == NULL) {
		mpsslog(PINFO, "%s: Could not open dir %s\n", mic->name, dirpath);
		return -1;
	}

	while ((tmp = readdir(dir))) {
		if (!strcmp(tmp->d_name, ".") || !strcmp(tmp->d_name, ".."))
			continue;

		snprintf(path, PATH_MAX - 1, "%s/%s", dirpath, tmp->d_name);

		if (lstat(path, &data) < 0) {
			mpsslog(PINFO, "%s: Couldn't lstat %s: %s\n", mic->name, path, strerror(errno));
			continue;
		}

		if (S_ISDIR(data.st_mode) && !S_ISLNK(data.st_mode)) {
			ssize_t dirsize;
			strcat(path, "/");
			if ((dirsize = get_dir_size(mic, path)) < 0) {
				mpsslog(PINFO, "%s: getting directory size failed %s %s\n",
					mic->name, path, strerror(errno));
				return dirsize;
			}
			result += dirsize;
		} else if (S_ISREG(data.st_mode)) {
			result += data.st_size;
		}
	}

	closedir(dir);

	return result;
}
/**
 * Check whether there is enough space on disk to create storage
 *
 * @param path Path to storage
 *
 * @return 1 if enough space exists, 0 otherwise
 */
int check_space(const char *path)
{
    struct statvfs stat;
    off_t size;
    int ret;

    ret = get_dir_size(path, &size);
    if (ret < 0) {
        LOGE("Failed to compute storage size %s", path);
        return ret;
    }

    ret = statvfs(path, &stat);
    if (ret < 0) {
        LOGE("Failed to compute free space for storage %s", path);
        return ret;
    }

    if ((unsigned)size < stat.f_bsize * stat.f_bfree)
        return 1;

    return 0;
}
Пример #6
0
static void delete_files(gpointer data, gpointer void_preserve_list)
{
    double cap_size;
    const char *dir = parse_size_pfx(&cap_size, data);
    GList *preserve_files_list = void_preserve_list;

    unsigned count = 100;
    while (--count != 0)
    {
        GList *worst_file_list = NULL;
        double cur_size = get_dir_size(dir, &worst_file_list, preserve_files_list);

        if (cur_size <= cap_size || !worst_file_list)
        {
            list_free_with_free(worst_file_list);
            log_info("cur_size:%.0f cap_size:%.0f, no (more) trimming", cur_size, cap_size);
            break;
        }

        /* Invert the list, so that largest/oldest file is first */
        worst_file_list = g_list_reverse(worst_file_list);
        /* And delete (some of) them */
        while (worst_file_list && cur_size > cap_size)
        {
            struct name_and_size *ns = worst_file_list->data;
            log_notice("%s is %.0f bytes (more than %.0f MB), deleting '%s' (%llu bytes)",
                    dir, cur_size, cap_size / (1024*1024), ns->name, (long long)ns->size);
            if (unlink(ns->name) != 0)
                perror_msg("Can't unlink '%s'", ns->name);
            else
                cur_size -= ns->size;
            free(ns);
            worst_file_list = g_list_delete_link(worst_file_list, worst_file_list);
        }
    }
}
static void delete_files(gpointer data, gpointer user_data_unused)
{
    double cap_size;
    const char *dir = parse_size_pfx(&cap_size, data);

    unsigned count = 1000;
    while (--count != 0)
    {
        char *worst_file = NULL;
        double worst_file_size = 0;
        double cur_size = get_dir_size(dir, &worst_file, &worst_file_size);
        if (cur_size <= cap_size)
        {
            VERB2 log("cur_size:%f cap_size:%f, no (more) trimming", cur_size, cap_size);
            free(worst_file);
            break;
        }
        log("%s is %.0f bytes (more than %.0f MB), deleting '%s'",
                dir, cur_size, cap_size / (1024*1024), worst_file);
        if (unlink(worst_file) != 0)
            perror_msg("Can't unlink '%s'", worst_file);
        free(worst_file);
    }
}
Пример #8
0
u64 fs::get_dir_size(const std::string& path)
{
	u64 result = 0;

	for (const auto entry : dir(path))
	{
		if (entry.name == "." || entry.name == "..")
		{
			continue;
		}

		if (entry.is_directory == false)
		{
			result += entry.size;
		}

		if (entry.is_directory == true)
		{
			result += get_dir_size(path + '/' + entry.name);
		}
	}

	return result;
}
/**
 * Copy multiple files from source to destination
 *
 * @param file_list File list
 * @param src_path Source
 * @param dst_path Destination
 *
 * @return 0 on success, negative value if an error occurs
 */
static int copy_files(file_info ** file_list, const char *src_path, const char *dst_path)
{
    file_info *iter = *file_list;
    char path[MAX_PATH_LENGTH + 1], buff[PROPERTY_VALUE_MAX], *linkname;
    char property[PROPERTY_KEY_MAX], path_hash_hex[ECRYPTFS_SIG_LEN * 2 + 1];
    unsigned char path_hash[ECRYPTFS_SIG_LEN];
    int len = 0, ret = -1;
    off64_t done = 0, total = 0;

    if (strlen(src_path) > MAX_PATH_LENGTH
        || strlen(dst_path) > MAX_PATH_LENGTH) {
        LOGE("Invalid arguments\n");
        return ret;
    }

    ret = get_dir_size(src_path, &total);
    if (ret < 0) {
        LOGE("Failed to compute storage size %s", src_path);
        return ret;
    }

    memset(property, 0, sizeof(property));
    memcpy(property, property_prefix, strlen(property_prefix));
    SHA512((unsigned char *)src_path, strlen(src_path), path_hash);
    convert_to_hex_format(path_hash, path_hash_hex, ECRYPTFS_SIG_LEN);
    memcpy(property + strlen(property_prefix), path_hash_hex, SHA_HEAD);

    memset(buff, 0, sizeof(buff));
    snprintf(buff, sizeof(buff), "%llu", (off64_t)(done / (double)total * 100));
    ret = property_set(property, buff);
    if (ret < 0) {
        LOGE("property_set");
    }

    while (iter) {
        len =
            strlen(dst_path) + strlen(iter->path) - strlen(src_path) +
            1;
        if (len > MAX_PATH_LENGTH) {
            LOGE("Invalig len\n");
            return -1;
        }

        strcpy(path, dst_path);
        path[strlen(dst_path)] = '/';
        strcpy(path + strlen(dst_path) + 1,
               iter->path + strlen(src_path));

        if (S_ISLNK(iter->st.st_mode)) {
            linkname = malloc(iter->st.st_size + 1);
            if (linkname == NULL) {
                LOGE("insufficient memory\n");
                exit(EXIT_FAILURE);
            }
            ret =
                readlink(iter->path, linkname,
                     iter->st.st_size + 1);
            if (ret < 0) {
                free(linkname);
                LOGE("lstat failed\n");
                return -1;
            }
            linkname[iter->st.st_size] = '\0';
            ret = symlink(linkname, path);
            if (ret < 0) {
                free(linkname);
                LOGE("can't create symlink %s", path);
                return ret;
            }

            ret = lsetfilecon(path, iter->con);
            if (ret < 0) {
                LOGE("lsetfilecon %s fail\n", iter->path);
                free(linkname);
                return ret;
            }

            ret = lchown(path, iter->st.st_uid, iter->st.st_gid);
            if (ret < 0) {
                LOGE("lchown %s fail\n", iter->path);
                free(linkname);
                return ret;
            }
            free(linkname);
            iter = iter->next;
            continue;
        }

        ret = copy_file(property, iter->path, path, &iter->st, iter->con, &done, &total);
        if (ret < 0) {
            LOGE("Copying file form %s to %s failed\n", iter->path,
                 path);
            return ret;
        }

        iter = iter->next;
    }
    return 0;
}
/**
 * Get size on disk for a directory
 * Similar functionality with du -c
 * @param path Directory path
 * @param size Directory size
 *
 * @return 0 for success, negative value in case of an error
 */
int get_dir_size(const char *path, off64_t * size)
{
    DIR *dir;
    struct dirent *dirent;
    struct stat st;
    int len, ret = -1;
    char *file_path;

    len = strlen(path);
    if (len > MAX_PATH_LENGTH) {
        LOGE("Invalid argument %s\n", path);
        return -1;
    }

    dir = opendir(path);
    if (!dir) {
        LOGE("open dir %s failed", path);
        return -1;
    }

    while ((dirent = readdir(dir))) {
        int file_len = strlen(dirent->d_name);

        /* This should not happen */
        if (file_len > MAX_FILE_LENGTH) {
            LOGE("file system error\n");
            closedir(dir);
            return -1;
        }

        /* ignore /. and /.. */
        if (file_len == 1 || file_len == 2) {
            if (dirent->d_name[0] == '.') {
                if (dirent->d_name[1] == '\0')
                    continue;
                if ((dirent->d_name[1] == '.')
                    && (dirent->d_name[2] == '\0'))
                    continue;
            }
        }

        file_path =
            (char *)malloc(MAX_PATH_LENGTH + MAX_FILE_LENGTH + 1);
        if (!file_path)
            return -1;
        memset(file_path, 0, MAX_PATH_LENGTH + MAX_FILE_LENGTH + 1);
        strcpy(file_path, path);
        *(file_path + len) = '/';
        strcpy(file_path + len + 1, dirent->d_name);

        ret = lstat(file_path, &st);
        if (ret < 0) {
            LOGE("lstat failed on %s\n", file_path);
            closedir(dir);
            free(file_path);
            return ret;
        }

        *size += st.st_size;

        if (dirent->d_type == DT_DIR) {
            ret = get_dir_size(file_path, size);
            if (ret < 0) {
                free(file_path);
                return ret;
            }
        }

        free(file_path);
    }

    closedir(dir);
    return 0;
}
Пример #11
0
static double get_dir_size(const char *dirname,
                GList **pp_worst_file_list,
                GList *preserve_files_list
) {
    DIR *dp = opendir(dirname);
    if (!dp)
        return 0;

    /* "now" is used only if caller wants to know worst_file */
    time_t now = pp_worst_file_list ? time(NULL) : 0;
    struct dirent *dent;
    double size = 0;
    while ((dent = readdir(dp)) != NULL)
    {
        if (dot_or_dotdot(dent->d_name))
            continue;

        char *fullname = concat_path_file(dirname, dent->d_name);
        struct stat stats;
        if (lstat(fullname, &stats) != 0)
            goto next;

        if (S_ISDIR(stats.st_mode))
        {
            double sz = get_dir_size(fullname, pp_worst_file_list, preserve_files_list);
            size += sz;
        }
        else if (S_ISREG(stats.st_mode) || S_ISLNK(stats.st_mode))
        {
            double sz = stats.st_size;
            /* Account for filename and inode storage (approximately).
             * This also makes even zero-length files to have nonzero cost.
             */
            sz += strlen(dent->d_name) + sizeof(stats);
            size += sz;

            if (pp_worst_file_list)
            {
                GList *cur = preserve_files_list;
                while (cur)
                {
                    //log_warning("'%s' ? '%s'", fullname, *pp);
                    if (strcmp(fullname, (char*)cur->data) == 0)
                        goto next;
                    cur = cur->next;
                }

                /* Calculate "weighted" size and age
                 * w = sz_kbytes * age_mins */
                sz /= 1024;
                long age = (now - stats.st_mtime) / 60;
                if (age > 1)
                    sz *= age;

                *pp_worst_file_list = insert_name_and_sizes(*pp_worst_file_list, fullname, sz, stats.st_size);
            }
        }
 next:
        free(fullname);
    }
    closedir(dp);

    return size;
}
/**
 * Encrypt Android user data
 *
 * @param user Android user id
 * @param password Android user password
 *
 * @return 0 on success, negative value on error
 */
int android_encrypt_user_data(int user, char *password)
{
    char data_path[MAX_PATH_LENGTH], media_path[MAX_PATH_LENGTH], buf[10];
    off_t size = 0, total_size = 0;
    int ret = -1;

    LOGI("Encrypt user data for %d", user);

    android_stop_services();
    sleep(5);

    memset(data_path, 0, sizeof(data_path));
    sprintf(data_path, "%s%d", ANDROID_USER_DATA_PATH, user);
    memset(media_path, 0, sizeof(media_path));
    sprintf(media_path, "%s%d", ANDROID_VIRTUAL_SDCARD_PATH, user);

    ret = get_dir_size(data_path, &size);
    if (ret < 0) {
        LOGE("Unable to get dir size for %s", data_path);
        return ret;
    }
    total_size += size;
    ret = get_dir_size(media_path, &size);
    if (ret < 0) {
        LOGE("Unable to get dir size for %s", media_path);
        return ret;
    }
    total_size += size;
    memset(buf, 0, sizeof(buf));
    snprintf(buf, sizeof(buf), "%llu", total_size);
    property_set("efs.encrypt.size", buf);

    if (user == PRIMARY_USER) {
        property_set("crypto.primary_user", "encrypting");
    }

    ret = EFS_create(data_path, password);
    if (ret < 0) {
        LOGE("Unable to create efs storage %s", data_path);
        return ret;
    }

    ret = EFS_create(media_path, password);
    if (ret < 0) {
        LOGE("Unable to create efs storage %s", media_path);
        return ret;
    }

    if (user == PRIMARY_USER) {
        android_reboot(ANDROID_RB_RESTART, 0, 0);
    }

    ret = android_unlock_user_data(user, password);
    if (ret < 0) {
        LOGE("Unable to unlock user data %d", user);
        return ret;
    }

    android_start_services();

    return 0;
}
Пример #13
0
void *
save_crashdump(void *arg)
{
	struct mic_info *mic = (struct mic_info *)arg;
	struct mpssd_info *mpssdi = (struct mpssd_info *)mic->data;
	int cdfd = -1;
	int procfd = -1;
	void *addr = NULL;
	ssize_t bytes;
	ssize_t total_bytes = 0;
	ssize_t dirlimit;
	ssize_t diractual;
	struct tm *tm = NULL;
	char pathname[PATH_MAX];
	time_t t;
	pid_t pid1 = 0;
	char *state;
	char *save;
	struct stat sbuf;
	struct statvfs vbuf;
	int err;

	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

	if ((dirlimit = atoi(CD_LIMIT) * (1024 * 1024 * 1024ULL)) == 0) {
		mpsslog(PWARN, "%s: [SaveCrashDump] Dump disabled\n", mic->name);
		goto reboot;
	}

	if (stat(CD_DIR, &sbuf) < 0) {
		if (mkdir(CD_DIR, 0755) < 0) {
			mpsslog(PWARN, "%s: [SaveCrashDump] Avborted - create directory %s failed: %s\n",
					mic->name, CD_DIR, strerror(errno));
			goto reboot;
		}

		diractual = dirlimit;
	} else {	/* Check size of crash directory with configured limits */
		if ((diractual = get_dir_size(mic, CD_DIR)) < 0) {
			mpsslog(PINFO, "%s: [SaveCrashDump] Avborted - get directory %s size failed: %s\n",
					mic->name, CD_DIR, strerror(errno));
			goto reboot;
		}
	}

	if (diractual > dirlimit) {
		mpsslog(PINFO, "%s: [SaveCrashDump] Avborted - %s current size 0x%lx configured limit 0x%lx\n",
			mic->name, CD_DIR, diractual, dirlimit);
		goto reboot;
	}

	/* Open core dump file with time details embedded in file name */
	time(&t);
	if ((tm = localtime(&t)) == 0) {
		mpsslog(PERROR, "%s: [SaveCrashdump] Aborted - get system date failed\n", mic->name);
		goto reboot;
	}

	/* Create crash directories if not done already */
	snprintf(pathname, PATH_MAX - 1, "%s/%s", CD_DIR, mic->name);
	if (mkdir(pathname, 0755) && errno != EEXIST) {
		mpsslog(PERROR, "%s: [SaveCrashDump] Aborted - create directory %s failed %s\n",
					mic->name, pathname, strerror(errno));
		goto reboot;
	}

	if (statvfs(pathname, &vbuf) < 0) {
		mpsslog(PERROR, "%s: [SaveCrashDump] Aborted - cannot read free disk size of %s: %s\n",
					mic->name, pathname, strerror(errno));
		goto reboot;
	}

	if (CD_MIN_DISK > (vbuf.f_bsize * vbuf.f_bfree)) {
		mpsslog(PERROR, "%s: [SaveCrashDump] Aborted - free disk space less than required 32Gb\n", mic->name);
		goto reboot;
	}

	/* Open vmcore entry for crashed card */
	snprintf(pathname, PATH_MAX - 1, "/proc/mic_vmcore/%s", mic->name);
	if ((procfd = open(pathname, O_RDONLY)) < 0) {
		mpsslog(PERROR, "%s: [SaveCrashdump] Aborted - open %s failed: %s\n",
					mic->name, pathname, strerror(errno));
		goto reboot;
	}

	snprintf(pathname, PATH_MAX - 1, "%s/%s/vmcore-%d-%d-%d-%d:%d:%d", CD_DIR, mic->name,
			tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
			tm->tm_hour, tm->tm_min, tm->tm_sec);

	if ((cdfd = open(pathname, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)) < 0) {
		mpsslog(PERROR, "%s: [SaveCrashDump] Aborted - open %s failed %s\n",
					mic->name, pathname, strerror(errno));
		goto cleanup1;
	}

	mpsslog(PINFO, "%s: [SaveCrashDump] Capturing uOS kernel crash dump\n", mic->name);

	/* Read from the proc entry and write to the core dump file */
	do {
		if (lseek(cdfd, CD_READ_CHUNK, SEEK_CUR) < 0) {
			mpsslog(PERROR, "%s: [SaveCrashDump] Aborted lseek failed %s\n", mic->name, strerror(errno));
			remove(pathname);
			goto cleanup2;
		}

		bytes = write(cdfd, "", 1);
		if (bytes != 1) {
			mpsslog(PERROR, "%s: [SaveCrashDump] Aborted write failed %s\n", mic->name, strerror(errno));
			remove(pathname);
			goto cleanup2;
		}

		if ((addr = mmap(NULL, CD_READ_CHUNK, PROT_READ|PROT_WRITE,
					MAP_SHARED, cdfd, total_bytes)) == MAP_FAILED) {
			mpsslog(PERROR, "%s: [SaveCrasdDump] Aborted mmap failed %s\n", mic->name, strerror(errno));
			remove(pathname);
			goto cleanup2;
		}

		if ((bytes = read(procfd, addr, CD_READ_CHUNK)) < 0) {
			mpsslog(PERROR, "%s: [SaveCrashDump] Aborted read failed %s\n", mic->name, strerror(errno));
			remove(pathname);
			munmap(addr, CD_READ_CHUNK);
			goto cleanup2;
		}

		total_bytes += bytes;
		munmap(addr, CD_READ_CHUNK);
		if (ftruncate(cdfd, total_bytes + 1) < 0) {
			mpsslog(PERROR, "%s: [SaveCrashDump] Aborted ftruncate failed %s\n", mic->name, strerror(errno));
			remove(pathname);
			goto cleanup2;
		}
	} while (bytes == CD_READ_CHUNK);

	mpsslog(PNORM, "%s: [SaveCrashDump] Completed raw dump size 0x%lx\n", mic->name, total_bytes);
	mpsslog(PNORM, "%s: [SaveCrashDump] Gzip started\n", mic->name);
	pid1 = gzip(pathname); /* Initiate compression of the file and reset MIC in parallel */


cleanup2:
	close(cdfd);

cleanup1:
	close(procfd);

reboot:
	if ((err = mpss_setsysfs(mic->name, "state", "reset:force")) != 0) {
		mpsslog(PINFO, "%s: [SaveCrashDump] Failed to set state sysfs - cannot reset: %s\n",
				mic->name, strerror(err));
		goto done;
	}

	if ((state = mpss_readsysfs(mic->name, "state")) == NULL) {
		mpsslog(PINFO, "%s: [SaveCrashDump] Failed to read state sysfs - state of reset unknown\n", mic->name);
		goto done;
	}

	while (strcmp(state, "ready") && strcmp(state, "reset failed")) {
		if (!strcmp(state, "online") || !strcmp(state, "booting")) {
			mpsslog(PINFO, "%s: [SaveCrashDump] External entity has already rebooted card\n", mic->name);
			free(state);
			goto done;
		}

		mpsslog(PINFO, "%s: [SaveCrashDump] Waiting for reset\n", mic->name);
		sleep(2);
		save = state;

		if ((state = mpss_readsysfs(mic->name, "state")) == NULL) {
			mpsslog(PWARN, "%s: [SaveCrashDump] wait for ready failed to read state sysfs - try again\n",
					mic->name);
			state = save;
		} else {
			free(save);
		}
	}

	if (strcmp(state, "ready")) {
		mpsslog(PERROR, "%s: [SaveCrashDump] Failed to reset card.  Aborting reboot\n", mic->name);
		free(state);
		goto done;
	}

	if (pid1 && (pid1 < 0 || ((waitpid(pid1, NULL, 0)) < 0)))
		remove(pathname);

	if (autoreboot(mic)) {
		while (pthread_mutex_lock(&start_lock) != 0);
		start_count++;
		while (pthread_mutex_lock(&mpssdi->pth_lock) != 0);
		pthread_create(&mpssdi->boot_pth, NULL, boot_mic, mic);
		while (pthread_mutex_unlock(&mpssdi->pth_lock) != 0);
		while (pthread_mutex_unlock(&start_lock) != 0);
	}
done:
	pthread_exit(NULL);
}