diff --git a/image.c b/image.c index 106c2833c..33e0d5b87 100644 --- a/image.c +++ b/image.c @@ -202,28 +202,48 @@ struct cr_imgset *cr_glob_imgset_open(int mode) return cr_imgset_open(-1 /* ignored */, GLOB, mode); } +static struct cr_img *do_open_image(struct cr_img *img, int dfd, int type, unsigned long flags, char *path); + struct cr_img *open_image_at(int dfd, int type, unsigned long flags, ...) { struct cr_img *img; - unsigned long oflags = flags; + unsigned long oflags; char path[PATH_MAX]; va_list args; - int ret; + bool lazy = false; - if (dfd == -1) + if (dfd == -1) { dfd = get_service_fd(IMG_FD_OFF); + lazy = (flags & O_CREAT); + } img = xmalloc(sizeof(*img)); if (!img) - goto errn; + return NULL; - oflags |= imgset_template[type].oflags; - flags &= ~(O_NOBUF); + oflags = flags | imgset_template[type].oflags; va_start(args, flags); vsnprintf(path, PATH_MAX, imgset_template[type].fmt, args); va_end(args); + if (lazy) { + img->fd = LAZY_IMG_FD; + img->type = type; + img->oflags = oflags; + img->path = xstrdup(path); + return img; + } + + return do_open_image(img, dfd, type, oflags, path); +} + +static struct cr_img *do_open_image(struct cr_img *img, int dfd, int type, unsigned long oflags, char *path) +{ + int ret, flags; + + flags = oflags & ~(O_NOBUF); + ret = openat(dfd, path, flags, CR_FD_PERM); if (ret < 0) { if (!(flags & O_CREAT) && (errno == ENOENT)) { @@ -271,17 +291,33 @@ skip_magic: err: xfree(img); -errn: return NULL; + err_close: close_image(img); return NULL; } +int open_image_lazy(struct cr_img *img) +{ + int dfd; + char *path = img->path; + + dfd = get_service_fd(IMG_FD_OFF); + if (do_open_image(img, dfd, img->type, img->oflags, path) == NULL) + return -1; + + xfree(path); + return 0; +} + void close_image(struct cr_img *img) { - if (!empty_image(img)) + if (lazy_image(img)) + xfree(img->path); + else if (!empty_image(img)) bclose(&img->_x); + xfree(img); } diff --git a/include/image.h b/include/image.h index 798e89179..ce9f95d51 100644 --- a/include/image.h +++ b/include/image.h @@ -124,16 +124,30 @@ extern bool ns_per_id; #define O_RSTR (O_RDONLY) struct cr_img { - struct bfd _x; + union { + struct bfd _x; + struct { + int fd; /* should be first to coincide with _x.fd */ + int type; + unsigned long oflags; + char *path; + }; + }; }; #define EMPTY_IMG_FD (-404) +#define LAZY_IMG_FD (-505) static inline bool empty_image(struct cr_img *img) { return img && img->_x.fd == EMPTY_IMG_FD; } +static inline bool lazy_image(struct cr_img *img) +{ + return img->_x.fd == LAZY_IMG_FD; +} + static inline int img_raw_fd(struct cr_img *img) { BUG_ON(bfd_buffered(&img->_x)); @@ -145,6 +159,7 @@ extern void close_image_dir(void); extern struct cr_img *open_image_at(int dfd, int type, unsigned long flags, ...); #define open_image(typ, flags, ...) open_image_at(-1, typ, flags, ##__VA_ARGS__) +extern int open_image_lazy(struct cr_img *img); extern struct cr_img *open_pages_image(unsigned long flags, struct cr_img *pmi); extern struct cr_img *open_pages_image_at(int dfd, unsigned long flags, struct cr_img *pmi); extern void up_page_ids_base(void); diff --git a/protobuf.c b/protobuf.c index 4a655795c..ae003da44 100644 --- a/protobuf.c +++ b/protobuf.c @@ -603,6 +603,9 @@ int pb_write_one(struct cr_img *img, void *obj, int type) return -1; } + if (lazy_image(img) && open_image_lazy(img)) + return -1; + size = cr_pb_descs[type].getpksize(obj); if (size > (u32)sizeof(local)) { buf = xmalloc(size);