/* * resume.c * * A simple user space resume handler for swsusp. * * Copyright (C) 2005 Rafael J. Wysocki * * This file is released under the GPLv2. * */ #include #include #include #include #include #include #include #include #include #include #include #include "swsusp.h" static char buffer[PAGE_SIZE]; /** * read_page - Read one page from a swap location. * @fd: File handle of the resume partition * @buf: Pointer to the area we're reading into. * @off: Swap offset of the place to read from. */ static int read_page(int fd, void *buf, unsigned long off) { off_t offset; int res = 0; ssize_t cnt = 0; if (off) { offset = off * PAGE_SIZE; if (lseek(fd, offset, SEEK_SET) == offset) cnt = read(fd, buf, PAGE_SIZE); if (cnt < PAGE_SIZE) { if (cnt < 0) res = cnt; else res = -EIO; } } return res; } /** * The following functions allow us to read data using a swap map * in a file-alike way */ static inline void release_swap_map_reader(struct swap_map_handle *handle) { if (handle->cur) free(handle->cur); handle->cur = NULL; } static inline int get_swap_map_reader(struct swap_map_handle *handle, int fd, unsigned long start) { int error; if (!start) return -EINVAL; handle->cur = (struct swap_map_page *)malloc(PAGE_SIZE); if (!handle->cur) return -ENOMEM; memset(handle->cur, 0, PAGE_SIZE); error = read_page(fd, handle->cur, start); if (error) { release_swap_map_reader(handle); return error; } handle->fd = fd; handle->k = 0; return 0; } static inline int swap_map_read_page(struct swap_map_handle *handle, void *buf) { unsigned long offset; int error; if (!handle->cur) return -EINVAL; offset = handle->cur->entries[handle->k]; if (!offset) return -EFAULT; error = read_page(handle->fd, buf, offset); if (error) return error; if (++handle->k >= MAP_PAGE_SIZE) { handle->k = 0; offset = handle->cur->next_swap; if (!offset) release_swap_map_reader(handle); else error = read_page(handle->fd, handle->cur, offset); } return error; } /** * load_image - load the image using the swap map handle * @handle and the snapshot handle @snapshot * (assume there are @nr_pages pages to load) */ static inline int load_image(struct swap_map_handle *handle, int dev, unsigned int nr_pages) { unsigned int m, n; int ret; int error = 0; printf("Loading image data pages (%u pages) ... ", nr_pages); m = nr_pages / 100; if (!m) m = 1; n = 0; do { error = swap_map_read_page(handle, buffer); if (error) break; if ((ret = write(dev, buffer, PAGE_SIZE)) < PAGE_SIZE) { error = ret < 0 ? ret : -ENOSPC; break; } if (!(n % m)) printf("\b\b\b\b%3d%%", n / m); n++; } while (n < nr_pages); if (!error) printf("\b\b\b\bdone\n"); return error; } static int read_image(int dev, char *resume_dev_name) { int fd, ret, error = 0; struct swap_map_handle handle; struct swsusp_info *header; unsigned int nr_pages; fd = open(resume_dev_name, O_RDWR | O_SYNC); if (fd < 0) { printf("resume: Could not open resume device\n"); return -ENOENT; } memset(&swsusp_header, 0, sizeof(swsusp_header)); ret = read(fd, &swsusp_header, PAGE_SIZE); if (ret == PAGE_SIZE) { if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) { memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10); /* Reset swap signature now */ if (lseek(fd, 0, SEEK_SET) == 0) { ret = write(fd, &swsusp_header, PAGE_SIZE); if (ret < PAGE_SIZE) error = ret < 0 ? ret : -EIO; } else { error = -EIO; } } else { error = -EINVAL; } } else { error = ret < 0 ? ret : -EIO; } if (!error) { error = get_swap_map_reader(&handle, fd, swsusp_header.image); if (!error) { header = (struct swsusp_info *)buffer; error = swap_map_read_page(&handle, header); } if (!error) { nr_pages = header->pages - 1; ret = write(dev, buffer, PAGE_SIZE); if (ret < PAGE_SIZE) error = ret < 0 ? ret : -EIO; } if (!error) error = load_image(&handle, dev, nr_pages); release_swap_map_reader(&handle); } fsync(fd); close(fd); if (!error) printf("resume: Reading resume file was successful\n"); else printf("resume: Error %d resuming\n", error); return error; } int main(int argc, char *argv[]) { char *snapshot_device_name, *resume_device_name; int dev; resume_device_name = argc <= 2 ? RESUME_DEVICE : argv[2]; snapshot_device_name = argc <= 1 ? SNAPSHOT_DEVICE : argv[1]; setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); if (mlockall(MCL_CURRENT | MCL_FUTURE)) { fprintf(stderr, "resume: Could not lock myself\n"); return 1; } dev = open(snapshot_device_name, O_WRONLY); if (dev < 0) return -ENOENT; if (read_image(dev, resume_device_name)) { fprintf(stderr, "resume: Could not read the image\n"); return errno; } if (freeze(dev)) { fprintf(stderr, "resume: Could not freeze processes\n"); return errno; } atomic_restore(dev); unfreeze(dev); close(dev); return 0; }