nathan@4
|
1 |
/* virtual.c
|
nathan@15
|
2 |
Copyright (c) 2000-2012 Craig Condit, Stefan Huelswitt.
|
nathan@4
|
3 |
|
nathan@4
|
4 |
Redistribution and use in source and binary forms, with or without
|
nathan@4
|
5 |
modification, are permitted provided that the following conditions are met:
|
nathan@4
|
6 |
|
nathan@4
|
7 |
1. Redistributions of source code must retain the above copyright notice,
|
nathan@4
|
8 |
this list of conditions and the following disclaimer.
|
nathan@4
|
9 |
2. Redistributions in binary form must reproduce the above copyright notice,
|
nathan@4
|
10 |
this list of conditions and the following disclaimer in the documentation
|
nathan@4
|
11 |
and/or other materials provided with the distribution.
|
nathan@4
|
12 |
|
nathan@4
|
13 |
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
nathan@4
|
14 |
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
nathan@4
|
15 |
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
nathan@4
|
16 |
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
|
nathan@4
|
17 |
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
nathan@4
|
18 |
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
nathan@4
|
19 |
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
nathan@4
|
20 |
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
nathan@4
|
21 |
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
nathan@4
|
22 |
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
nathan@4
|
23 |
SUCH DAMAGE.
|
nathan@4
|
24 |
*/
|
nathan@4
|
25 |
|
nathan@4
|
26 |
#define _LARGEFILE64_SOURCE
|
nathan@4
|
27 |
#define _GNU_SOURCE
|
nathan@4
|
28 |
|
nathan@4
|
29 |
#include <stdlib.h>
|
nathan@4
|
30 |
#include <stdio.h>
|
nathan@4
|
31 |
#include <unistd.h>
|
nathan@4
|
32 |
#include <fcntl.h>
|
nathan@4
|
33 |
#include <errno.h>
|
nathan@4
|
34 |
#include <string.h>
|
nathan@4
|
35 |
#include <sys/wait.h>
|
nathan@4
|
36 |
#include <sys/stat.h>
|
nathan@4
|
37 |
|
nathan@4
|
38 |
#include "virtual.h"
|
nathan@4
|
39 |
#include "cdbackup.h"
|
nathan@4
|
40 |
#include "cdrom.h"
|
nathan@4
|
41 |
#include "misc.h"
|
nathan@4
|
42 |
#include "debug.h"
|
nathan@4
|
43 |
|
nathan@4
|
44 |
int fd=-1;
|
nathan@4
|
45 |
struct toc_entry *toc=0;
|
nathan@4
|
46 |
long long cd_used, cd_avail;
|
nathan@4
|
47 |
|
nathan@4
|
48 |
static struct cd_header cd_header;
|
nathan@4
|
49 |
|
nathan@4
|
50 |
static unsigned char virt_buffer[VIRT_HEADER_LEN];
|
nathan@4
|
51 |
struct virt_header *virt_header=(struct virt_header *)virt_buffer;
|
nathan@6
|
52 |
int virtualMissing=0, virt_off=-1, virt_regular=0;
|
nathan@4
|
53 |
char *real_virt_name=0;
|
nathan@4
|
54 |
|
nathan@4
|
55 |
extern int virtual;
|
nathan@4
|
56 |
extern int disknum;
|
nathan@4
|
57 |
extern char *virt_name, *cd_dev;
|
nathan@4
|
58 |
|
nathan@4
|
59 |
/****************************************************************************/
|
nathan@4
|
60 |
|
nathan@4
|
61 |
void Vopen(int ro)
|
nathan@4
|
62 |
{
|
nathan@4
|
63 |
Vclose();
|
nathan@4
|
64 |
if(!virtual) {
|
nathan@4
|
65 |
if((fd=open64(cd_dev,O_RDONLY|O_NONBLOCK))<0)
|
nathan@4
|
66 |
error("Open failed (device)");
|
nathan@4
|
67 |
}
|
nathan@4
|
68 |
else {
|
nathan@4
|
69 |
free(real_virt_name);
|
nathan@6
|
70 |
if(disknum==1 || !virt_regular) {
|
nathan@4
|
71 |
real_virt_name=strdup(virt_name);
|
nathan@4
|
72 |
}
|
nathan@4
|
73 |
else if(virt_off>0) {
|
nathan@4
|
74 |
char *strip=strdup(virt_name);
|
nathan@4
|
75 |
char *dot=rindex(strip,'.');
|
nathan@4
|
76 |
if(dot) {
|
nathan@4
|
77 |
*dot=0;
|
nathan@13
|
78 |
if(asprintf(&real_virt_name,"%s.%d",strip,disknum+virt_off)<0)
|
nathan@13
|
79 |
error("error making virtual name");
|
nathan@4
|
80 |
}
|
nathan@4
|
81 |
else serror("Bad filename format");
|
nathan@4
|
82 |
free(strip);
|
nathan@4
|
83 |
}
|
nathan@4
|
84 |
else {
|
nathan@13
|
85 |
if(asprintf(&real_virt_name,"%s.%d",virt_name,disknum)<0)
|
nathan@13
|
86 |
error("error making virtual name");
|
nathan@4
|
87 |
}
|
nathan@4
|
88 |
DEBUG("Vopen: real filename is '%s' disknum=%d virt_off=%d\n",
|
nathan@4
|
89 |
real_virt_name,disknum,virt_off);
|
nathan@4
|
90 |
virtualMissing=0; virt_regular=0;
|
nathan@4
|
91 |
if((fd=open64(real_virt_name,ro ? O_RDONLY:O_RDWR))<0) {
|
nathan@4
|
92 |
if(errno==EACCES || errno==ENOENT || errno==ENOTDIR) {
|
nathan@4
|
93 |
virtualMissing=1; virt_regular=1;
|
nathan@4
|
94 |
DEBUG("Vopen: missing virtual image, assuming an empty one\n");
|
nathan@4
|
95 |
}
|
nathan@4
|
96 |
else error("Open failed (virtual)");
|
nathan@4
|
97 |
}
|
nathan@4
|
98 |
else {
|
nathan@4
|
99 |
struct stat64 st;
|
nathan@4
|
100 |
if(fstat64(fd,&st)<0) error("Stat failed (virtual)");
|
nathan@4
|
101 |
if(S_ISREG(st.st_mode)) virt_regular=1;
|
nathan@4
|
102 |
}
|
nathan@4
|
103 |
}
|
nathan@4
|
104 |
}
|
nathan@4
|
105 |
|
nathan@4
|
106 |
/****************************************************************************/
|
nathan@4
|
107 |
|
nathan@4
|
108 |
void Vclose(void)
|
nathan@4
|
109 |
{
|
nathan@4
|
110 |
if(fd>=0) {
|
nathan@4
|
111 |
close(fd);
|
nathan@4
|
112 |
fd=-1;
|
nathan@4
|
113 |
}
|
nathan@4
|
114 |
}
|
nathan@4
|
115 |
|
nathan@4
|
116 |
/****************************************************************************/
|
nathan@4
|
117 |
|
nathan@4
|
118 |
int VisRegular(void)
|
nathan@4
|
119 |
{
|
nathan@4
|
120 |
return virt_regular;
|
nathan@4
|
121 |
}
|
nathan@4
|
122 |
|
nathan@4
|
123 |
/****************************************************************************/
|
nathan@4
|
124 |
|
nathan@4
|
125 |
static int VgetCdHeader(struct cd_header *cd)
|
nathan@4
|
126 |
{
|
nathan@4
|
127 |
if(virtualMissing) {
|
nathan@4
|
128 |
if(verbose)
|
nathan@4
|
129 |
fprintf(stderr,"%s: Unable to get virtual image header, assuming new virtual image\n",prg_name);
|
nathan@4
|
130 |
memset(virt_buffer,0,VIRT_HEADER_LEN);
|
nathan@4
|
131 |
virt_header->magic=VIRT_MAGIC;
|
nathan@4
|
132 |
virt_header->version=VIRT_VERSION;
|
nathan@4
|
133 |
virt_header->leadout=1;
|
nathan@4
|
134 |
virt_header->count=disknum + (virt_off>0 ? virt_off:0);
|
nathan@4
|
135 |
}
|
nathan@4
|
136 |
else {
|
nathan@4
|
137 |
int n;
|
nathan@4
|
138 |
if((n=read(fd,virt_buffer,VIRT_HEADER_LEN))<0)
|
nathan@4
|
139 |
error("Read failed (virtual header)");
|
nathan@4
|
140 |
if(n!=VIRT_HEADER_LEN)
|
nathan@4
|
141 |
serror("Short read on virtual header");
|
nathan@4
|
142 |
if(virt_header->magic!=VIRT_MAGIC)
|
nathan@4
|
143 |
serror("Missing magic value in virtual header. Really a virtual image?");
|
nathan@4
|
144 |
if(virt_header->version>VIRT_VERSION)
|
nathan@4
|
145 |
serror("Don't know how to handle this virtual image version");
|
nathan@4
|
146 |
}
|
nathan@4
|
147 |
|
nathan@4
|
148 |
if(virt_off<0 && disknum==1) {
|
nathan@4
|
149 |
virt_off=virt_header->count-1;
|
nathan@4
|
150 |
DEBUG("VgetCdHeader: setting virt_off=%d\n",virt_off);
|
nathan@4
|
151 |
}
|
nathan@4
|
152 |
cd->start_track=1; cd->end_track=virt_header->tracks;
|
nathan@4
|
153 |
cd->used=virt_header->leadout;
|
nathan@4
|
154 |
return cd->end_track;
|
nathan@4
|
155 |
}
|
nathan@4
|
156 |
|
nathan@4
|
157 |
/****************************************************************************/
|
nathan@4
|
158 |
|
nathan@4
|
159 |
static void VgetCdTrack(int num, struct cd_track *cd)
|
nathan@4
|
160 |
{
|
nathan@4
|
161 |
cd->start_sec=virt_header->start[num-1];
|
nathan@4
|
162 |
cd->leadout_size=0;
|
nathan@4
|
163 |
cd->is_data=1;
|
nathan@4
|
164 |
}
|
nathan@4
|
165 |
|
nathan@4
|
166 |
/****************************************************************************/
|
nathan@4
|
167 |
|
nathan@4
|
168 |
int VreadToc(int trackInfos)
|
nathan@4
|
169 |
{
|
nathan@4
|
170 |
struct cd_track cd_track;
|
nathan@4
|
171 |
int i, tracks;
|
nathan@4
|
172 |
|
nathan@4
|
173 |
tracks=virtual ? VgetCdHeader(&cd_header) : getCdHeader(&cd_header);
|
nathan@4
|
174 |
if(!tracks) {
|
nathan@4
|
175 |
cd_used=0; cd_avail=(long long)cd_len*CD_FRAMESIZE;
|
nathan@4
|
176 |
DEBUG("Vreadtoc: empty media\n");
|
nathan@4
|
177 |
return 0;
|
nathan@4
|
178 |
}
|
nathan@4
|
179 |
DEBUG("Vreadtoc: starttrack=%d endtrack=%d tracks=%d\n",
|
nathan@4
|
180 |
cd_header.start_track,cd_header.end_track,tracks);
|
nathan@4
|
181 |
|
nathan@4
|
182 |
cd_used =(long long)cd_header.used*CD_FRAMESIZE;
|
nathan@4
|
183 |
cd_avail =(long long)cd_len*CD_FRAMESIZE-cd_used;
|
nathan@4
|
184 |
if(cd_avail<0) cd_avail=0;
|
nathan@4
|
185 |
DEBUG("Vreadtoc: cd_used=%lld (%lld secs) cd_avail=%lld (%lld secs)\n",
|
nathan@4
|
186 |
cd_used,cd_used/CD_FRAMESIZE,cd_avail,cd_avail/CD_FRAMESIZE);
|
nathan@4
|
187 |
|
nathan@4
|
188 |
free(toc);
|
nathan@4
|
189 |
if(!(toc=calloc(tracks,sizeof(struct toc_entry)))) serror("No memory for TOC");
|
nathan@4
|
190 |
|
nathan@4
|
191 |
cd_track.start_track=cd_header.start_track;
|
nathan@4
|
192 |
for(i=tracks-1; i>=0; i--) {
|
nathan@4
|
193 |
int t=cd_header.start_track+i;
|
nathan@4
|
194 |
if(virtual) VgetCdTrack(t,&cd_track); else getCdTrack(t,&cd_track);
|
nathan@4
|
195 |
toc[i].track_no=t;
|
nathan@4
|
196 |
toc[i].sec_start=cd_track.start_sec;
|
nathan@4
|
197 |
toc[i].sec_end=((i==tracks-1) ? cd_header.used : toc[i+1].sec_start)-1-cd_track.leadout_size;
|
nathan@4
|
198 |
toc[i].is_data=cd_track.is_data;
|
nathan@4
|
199 |
DEBUG("Vreadtoc: index=%d track=%d sec_start=%d sec_end=%d data=%d\n",
|
nathan@4
|
200 |
i,t,toc[i].sec_start,toc[i].sec_end,toc[i].is_data);
|
nathan@4
|
201 |
}
|
nathan@4
|
202 |
|
nathan@4
|
203 |
if(trackInfos) {
|
nathan@4
|
204 |
for(i=0; i<tracks; i++) {
|
nathan@4
|
205 |
char inbuffer[CD_FRAMESIZE];
|
nathan@4
|
206 |
struct header_block *track_header=(struct header_block *)inbuffer;
|
nathan@4
|
207 |
|
nathan@4
|
208 |
if(toc[i].is_data) {
|
nathan@4
|
209 |
Vseek(i);
|
nathan@4
|
210 |
Vread(inbuffer);
|
nathan@4
|
211 |
if(!strncmp(SHORT_HDR,track_header->id_str,strlen(SHORT_HDR))) {
|
nathan@4
|
212 |
toc[i].is_cdbackup=1;
|
nathan@4
|
213 |
strncpy(toc[i].id_str,track_header->id_str,32); toc[i].id_str[32]=0;
|
nathan@4
|
214 |
strncpy(toc[i].vol_id, track_header->vol_id,32); toc[i].vol_id[32]=0;
|
nathan@4
|
215 |
strncpy(toc[i].t_stamp, track_header->t_stamp,12); toc[i].t_stamp[12]=0;
|
nathan@4
|
216 |
toc[i].disk_set = track_header->disk_set;
|
nathan@4
|
217 |
toc[i].flags = track_header->flags;
|
nathan@4
|
218 |
}
|
nathan@4
|
219 |
}
|
nathan@4
|
220 |
}
|
nathan@4
|
221 |
}
|
nathan@4
|
222 |
|
nathan@4
|
223 |
return tracks;
|
nathan@4
|
224 |
}
|
nathan@4
|
225 |
|
nathan@4
|
226 |
/****************************************************************************/
|
nathan@4
|
227 |
|
nathan@4
|
228 |
const char *VdevName(void)
|
nathan@4
|
229 |
{
|
nathan@4
|
230 |
static char buff[80];
|
nathan@4
|
231 |
if(virtual) snprintf(buff,sizeof(buff),"image %s",virt_name);
|
nathan@4
|
232 |
else snprintf(buff,sizeof(buff),"device %s",cd_dev);
|
nathan@4
|
233 |
return buff;
|
nathan@4
|
234 |
}
|
nathan@4
|
235 |
|
nathan@4
|
236 |
/****************************************************************************/
|
nathan@4
|
237 |
|
nathan@4
|
238 |
void VprintSpace(void)
|
nathan@4
|
239 |
{
|
nathan@4
|
240 |
if(verbose) {
|
nathan@4
|
241 |
char flex1[16], flex2[16], flex3[16];
|
nathan@4
|
242 |
fprintf(stderr,
|
nathan@4
|
243 |
"Disk size: %s (%7ld blocks)\n"
|
nathan@4
|
244 |
"Space used: %s (%7lld blocks)\n"
|
nathan@4
|
245 |
"Space avail:%s (%7lld blocks)\n",
|
nathan@4
|
246 |
FlexSize(flex1,(long long)cd_len*CD_FRAMESIZE),cd_len,
|
nathan@4
|
247 |
FlexSize(flex2,cd_used), cd_used/CD_FRAMESIZE,
|
nathan@4
|
248 |
FlexSize(flex3,cd_avail), cd_avail/CD_FRAMESIZE);
|
nathan@4
|
249 |
}
|
nathan@4
|
250 |
}
|
nathan@4
|
251 |
|
nathan@4
|
252 |
/****************************************************************************/
|
nathan@4
|
253 |
|
nathan@4
|
254 |
long long Vseek(int trackNum)
|
nathan@4
|
255 |
{
|
nathan@4
|
256 |
long long pos=0;
|
nathan@4
|
257 |
if(trackNum>=0) pos=(long long)toc[trackNum].sec_start*CD_FRAMESIZE;
|
nathan@4
|
258 |
DEBUG("Vseek: seeking to index %d, track %d, offset %lld (%s)\n",
|
nathan@4
|
259 |
trackNum,toc[trackNum].track_no,pos,virtual ? "virtual":"real");
|
nathan@4
|
260 |
if(lseek64(fd,pos,SEEK_SET)<0) error("Seek failed");
|
nathan@4
|
261 |
return pos;
|
nathan@4
|
262 |
}
|
nathan@4
|
263 |
|
nathan@4
|
264 |
/****************************************************************************/
|
nathan@4
|
265 |
|
nathan@4
|
266 |
void Vread(void *buf)
|
nathan@4
|
267 |
{
|
nathan@4
|
268 |
if(full_read(fd,buf,CD_FRAMESIZE)!=CD_FRAMESIZE)
|
nathan@4
|
269 |
serror("Unexpected EOF reading data");
|
nathan@4
|
270 |
}
|
nathan@4
|
271 |
|
nathan@4
|
272 |
/****************************************************************************/
|
nathan@4
|
273 |
|
nathan@4
|
274 |
static unsigned long r_ahead, r_fahead;
|
nathan@4
|
275 |
|
nathan@4
|
276 |
void VgetAhead(void)
|
nathan@4
|
277 |
{
|
nathan@4
|
278 |
if(!virtual) get_param(fd,&r_ahead,&r_fahead);
|
nathan@4
|
279 |
}
|
nathan@4
|
280 |
|
nathan@4
|
281 |
/****************************************************************************/
|
nathan@4
|
282 |
|
nathan@4
|
283 |
void VsetAhead(int restore)
|
nathan@4
|
284 |
{
|
nathan@4
|
285 |
if(!virtual) {
|
nathan@4
|
286 |
if(restore) set_param(fd,r_ahead,r_fahead);
|
nathan@4
|
287 |
else set_param(fd,0,0);
|
nathan@4
|
288 |
}
|
nathan@4
|
289 |
}
|