1 /*
2  * Copyright (c) 2007, XenSource Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of XenSource Inc. nor the names of its contributors
13  *       may be used to endorse or promote products derived from this software
14  *       without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
20  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <sys/statvfs.h>
35 #include <sys/stat.h>
36 #include <sys/ioctl.h>
37 #include <string.h>
38 
39 #include "blk.h"
40 #include "tapdisk.h"
41 #include "tapdisk-driver.h"
42 #include "tapdisk-interface.h"
43 
44 char *img;
45 long int   disksector_size;
46 long int   disksize;
47 long int   diskinfo;
48 static int connections = 0;
49 
50 struct tdram_state {
51         int fd;
52 };
53 
54 /*Get Image size, secsize*/
get_image_info(int fd,td_disk_info_t * info)55 static int get_image_info(int fd, td_disk_info_t *info)
56 {
57 	int ret;
58 	long size;
59 	unsigned long total_size;
60 	struct statvfs statBuf;
61 	struct stat stat;
62 
63 	ret = fstat(fd, &stat);
64 	if (ret != 0) {
65 		DPRINTF("ERROR: fstat failed, Couldn't stat image");
66 		return -EINVAL;
67 	}
68 
69 	if (S_ISBLK(stat.st_mode)) {
70 		/*Accessing block device directly*/
71 		info->size = 0;
72 		if (blk_getimagesize(fd, &info->size) != 0)
73 			return -EINVAL;
74 
75 		DPRINTF("Image size: \n\tpre sector_shift  [%llu]\n\tpost "
76 			"sector_shift [%llu]\n",
77 			(long long unsigned)(info->size << SECTOR_SHIFT),
78 			(long long unsigned)info->size);
79 
80 		/*Get the sector size*/
81 		if (blk_getsectorsize(fd, &info->sector_size) != 0)
82 			info->sector_size = DEFAULT_SECTOR_SIZE;
83 
84 	} else {
85 		/*Local file? try fstat instead*/
86 		info->size = (stat.st_size >> SECTOR_SHIFT);
87 		info->sector_size = DEFAULT_SECTOR_SIZE;
88 		DPRINTF("Image size: \n\tpre sector_shift  [%llu]\n\tpost "
89 			"sector_shift [%llu]\n",
90 			(long long unsigned)(info->size << SECTOR_SHIFT),
91 			(long long unsigned)info->size);
92 	}
93 
94 	if (info->size == 0) {
95 		info->size =((uint64_t) MAX_RAMDISK_SIZE);
96 		info->sector_size = DEFAULT_SECTOR_SIZE;
97 	}
98 	info->info = 0;
99 
100         /*Store variables locally*/
101 	disksector_size = info->sector_size;
102 	disksize        = info->size;
103 	diskinfo        = info->info;
104 	DPRINTF("Image sector_size: \n\t[%"PRIu64"]\n",
105 		info->sector_size);
106 
107 	return 0;
108 }
109 
110 /* Open the disk file and initialize ram state. */
tdram_open(td_driver_t * driver,const char * name,td_flag_t flags)111 int tdram_open (td_driver_t *driver, const char *name, td_flag_t flags)
112 {
113 	char *p;
114 	uint64_t size;
115 	int i, fd, ret = 0, count = 0, o_flags;
116 	struct tdram_state *prv = (struct tdram_state *)driver->data;
117 
118 	connections++;
119 
120 	if (connections > 1) {
121 		driver->info.sector_size = disksector_size;
122 		driver->info.size        = disksize;
123 		driver->info.info        = diskinfo;
124 		DPRINTF("Image already open, returning parameters:\n");
125 		DPRINTF("Image size: \n\tpre sector_shift  [%llu]\n\tpost "
126 			"sector_shift [%llu]\n",
127 			(long long unsigned)(driver->info.size << SECTOR_SHIFT),
128 			(long long unsigned)driver->info.size);
129 		DPRINTF("Image sector_size: \n\t[%"PRIu64"]\n",
130 			driver->info.sector_size);
131 
132 		prv->fd = -1;
133 		goto done;
134 	}
135 
136 	/* Open the file */
137 	o_flags = O_DIRECT | O_LARGEFILE |
138 		((flags == TD_OPEN_RDONLY) ? O_RDONLY : O_RDWR);
139         fd = open(name, o_flags);
140 
141         if ((fd == -1) && (errno == EINVAL)) {
142 
143                 /* Maybe O_DIRECT isn't supported. */
144 		o_flags &= ~O_DIRECT;
145                 fd = open(name, o_flags);
146                 if (fd != -1) DPRINTF("WARNING: Accessing image without"
147                                      "O_DIRECT! (%s)\n", name);
148 
149         } else if (fd != -1) DPRINTF("open(%s) with O_DIRECT\n", name);
150 
151         if (fd == -1) {
152 		DPRINTF("Unable to open [%s]!\n",name);
153         	ret = 0 - errno;
154         	goto done;
155         }
156 
157         prv->fd = fd;
158 
159 	ret = get_image_info(fd, &driver->info);
160 	size = MAX_RAMDISK_SIZE;
161 
162 	if (driver->info.size > size) {
163 		DPRINTF("Disk exceeds limit, must be less than [%d]MB",
164 			(MAX_RAMDISK_SIZE<<SECTOR_SHIFT)>>20);
165 		return -ENOMEM;
166 	}
167 
168 	/*Read the image into memory*/
169 	if (posix_memalign((void **)&img,
170 			   DEFAULT_SECTOR_SIZE,
171 			   driver->info.size << SECTOR_SHIFT)) {
172 		DPRINTF("Mem malloc failed\n");
173 		return -errno;
174 	}
175 	p = img;
176 	DPRINTF("Reading %llu bytes.......",
177 		(long long unsigned)driver->info.size << SECTOR_SHIFT);
178 
179 	for (i = 0; i < driver->info.size; i++) {
180 		ret = read(prv->fd, p, driver->info.sector_size);
181 		if (ret != driver->info.sector_size) {
182 			DPRINTF("ret = %d, errno = %d\n", ret, errno);
183 			ret = 0 - errno;
184 			break;
185 		} else {
186 			count += ret;
187 			p = img + count;
188 		}
189 	}
190 	DPRINTF("[%d]\n",count);
191 	if (count != driver->info.size << SECTOR_SHIFT) {
192 		ret = -1;
193 	} else {
194 		ret = 0;
195 	}
196 
197 done:
198 	return ret;
199 }
200 
tdram_queue_read(td_driver_t * driver,td_request_t treq)201 void tdram_queue_read(td_driver_t *driver, td_request_t treq)
202 {
203 	struct tdram_state *prv = (struct tdram_state *)driver->data;
204 	int      size    = treq.secs * driver->info.sector_size;
205 	uint64_t offset  = treq.sec * (uint64_t)driver->info.sector_size;
206 
207 	memcpy(treq.buf, img + offset, size);
208 
209 	td_complete_request(treq, 0);
210 }
211 
tdram_queue_write(td_driver_t * driver,td_request_t treq)212 void tdram_queue_write(td_driver_t *driver, td_request_t treq)
213 {
214 	struct tdram_state *prv = (struct tdram_state *)driver->data;
215 	int      size    = treq.secs * driver->info.sector_size;
216 	uint64_t offset  = treq.sec * (uint64_t)driver->info.sector_size;
217 
218 	/* We assume that write access is controlled
219 	 * at a higher level for multiple disks */
220 	memcpy(img + offset, treq.buf, size);
221 
222 	td_complete_request(treq, 0);
223 }
224 
tdram_close(td_driver_t * driver)225 int tdram_close(td_driver_t *driver)
226 {
227 	struct tdram_state *prv = (struct tdram_state *)driver->data;
228 
229 	connections--;
230 
231 	return 0;
232 }
233 
tdram_get_parent_id(td_driver_t * driver,td_disk_id_t * id)234 int tdram_get_parent_id(td_driver_t *driver, td_disk_id_t *id)
235 {
236 	return TD_NO_PARENT;
237 }
238 
tdram_validate_parent(td_driver_t * driver,td_driver_t * pdriver,td_flag_t flags)239 int tdram_validate_parent(td_driver_t *driver,
240 			  td_driver_t *pdriver, td_flag_t flags)
241 {
242 	return -EINVAL;
243 }
244 
245 struct tap_disk tapdisk_ram = {
246 	.disk_type          = "tapdisk_ram",
247 	.flags              = 0,
248 	.private_data_size  = sizeof(struct tdram_state),
249 	.td_open            = tdram_open,
250 	.td_close           = tdram_close,
251 	.td_queue_read      = tdram_queue_read,
252 	.td_queue_write     = tdram_queue_write,
253 	.td_get_parent_id   = tdram_get_parent_id,
254 	.td_validate_parent = tdram_validate_parent,
255 	.td_debug           = NULL,
256 };
257