1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <unistd.h>
5#include <fcntl.h>
6#include <err.h>
7#include <errno.h>
8#include <sys/stat.h>
9#include <sys/fcntl.h>
10#include <sys/disk.h>
11
12#include "hfsmeta.h"
13
14/*
15 * Functions to wrap around a device.
16 */
17#define MIN(a, b) \
18	({ __typeof(a) __a = (a); __typeof(b) __b = (b); \
19		__a < __b ? __a : __b; })
20
21struct DeviceWrapperContext {
22	char *pathname;
23	size_t blockSize;
24	off_t devSize;
25	int fd;
26};
27
28static int
29noClean(struct IOWrapper *ctx)
30{
31	// Conceivably, we could erase the entire device
32	return 0;
33}
34
35static ssize_t
36doRead(struct IOWrapper *ctx, off_t start, void *buffer, off_t len)
37{
38	// For now, just do a pread
39	struct DeviceWrapperContext *dctx = (struct DeviceWrapperContext*)ctx->context;
40
41	return pread(dctx->fd, buffer, (size_t)len, start);
42}
43
44static ssize_t
45writeExtent(struct IOWrapper *context, DeviceInfo_t *devp, off_t start, off_t len, void (^bp)(off_t))
46{
47	const size_t bufSize = 1024 * 1024;
48	struct DeviceWrapperContext *ctx = (struct DeviceWrapperContext*)context->context;
49	uint8_t *buffer = NULL;
50	ssize_t retval = 0;
51	off_t total = 0;
52
53	if (debug) printf("Writing extent <%lld, %lld> to device %s", start, len, ctx->pathname);
54
55	buffer = malloc(bufSize);
56	if (buffer == NULL) {
57		warn("%s(%s): Could not allocate %zu bytes for buffer", __FILE__, __FUNCTION__, bufSize);
58		retval = -1;
59		goto done;
60	}
61
62	while (total < len) {
63		ssize_t nread;
64		size_t amt = MIN(bufSize, len - total);
65		// XXX - currently, DeviceWrapepr isn't used, but it needs to deal wit unaligned I/O when it is.
66		nread = pread(devp->fd, buffer, amt, start + total);
67		if (nread == -1) {
68			warn("Cannot read from device at offset %lld", start + total);
69			retval = -1;
70			goto done;
71		}
72		(void)pwrite(ctx->fd, (char*)buffer, nread, start + total);
73		bp(nread);
74		total += nread;
75	}
76done:
77	if (buffer)
78		free(buffer);
79	return retval;
80}
81
82/*
83 * Device files can't have progress information stored, so we don't do anything.
84 */
85static off_t
86GetProgress(struct IOWrapper *context)
87{
88	return 0;
89}
90static void
91SetProgress(struct IOWrapper *context, off_t progr)
92{
93	return;
94}
95
96struct IOWrapper *
97InitDeviceWrapper(const char *path, DeviceInfo_t *devp)
98{
99	struct DeviceWrapperContext ctx = { 0 };
100	struct DeviceWrapperContext *retctx = NULL;
101	IOWrapper_t *retval = NULL;
102	struct stat sb;
103	uint64_t blockCount;
104	char rawname[strlen(path) + 2];	// /dev/disk5 -> /dev/rdisk5
105
106	if (strncmp(path, "/dev/disk", 9) == 0) {
107		// Need to make it into a raw device name
108		sprintf(rawname, "/dev/rdisk%s", path + 9);
109	} else {
110		strcpy(rawname, path);
111	}
112
113	if (lstat(rawname, &sb) == -1) {
114		warn("cannot examine raw device %s", rawname);
115		goto done;
116	}
117	if ((sb.st_mode & S_IFMT) != S_IFCHR) {
118		warnx("device %s is not a raw device", rawname);
119		goto done;
120	}
121
122	ctx.pathname = strdup(rawname);
123
124	ctx.fd = open(rawname, O_RDWR);
125	if (ctx.fd == -1) {
126		warn("Cannot open device %s for reading and writing", rawname);
127		goto done;
128	}
129
130	if (ioctl(ctx.fd, DKIOCGETBLOCKSIZE, &ctx.blockSize) == -1) {
131		ctx.blockSize = 512;	// A reasonable default
132	}
133	if (ioctl(ctx.fd, DKIOCGETBLOCKCOUNT, &blockCount) == -1) {
134		warn("Cannot block count for device %s", rawname);
135		goto done;
136	}
137	ctx.devSize = ctx.blockSize * blockCount;
138
139	if (ctx.devSize != devp->size) {
140		warnx("Device %s is not the same size (%lld) as source device (%lld)", rawname, ctx.devSize, devp->size);
141		goto done;
142	}
143
144	ctx.pathname = strdup(rawname);
145	retctx = malloc(sizeof(ctx));
146	if (retctx == NULL) {
147		warn("Cannot allocate space for device context");
148		goto done;
149	}
150	*retctx = ctx;
151	retval = malloc(sizeof(*retval));
152	if (retval == NULL) {
153		warn("Cannot allocate space for device wrapper");
154		goto done;
155	}
156	retval->context = retctx;
157	retval->reader = &doRead;
158	retval->writer = &writeExtent;
159	retval->getprog = &GetProgress;
160	retval->setprog = &SetProgress;
161	retval->cleanup = &noClean;
162
163done:
164	return retval;
165}
166