nathan@4: /* virtual.c nathan@4: Copyright (c) 2000-2004 Craig Condit, Stefan Hülswitt. nathan@4: nathan@4: Redistribution and use in source and binary forms, with or without nathan@4: modification, are permitted provided that the following conditions are met: nathan@4: nathan@4: 1. Redistributions of source code must retain the above copyright notice, nathan@4: this list of conditions and the following disclaimer. nathan@4: 2. Redistributions in binary form must reproduce the above copyright notice, nathan@4: this list of conditions and the following disclaimer in the documentation nathan@4: and/or other materials provided with the distribution. nathan@4: nathan@4: THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS nathan@4: OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED nathan@4: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE nathan@4: DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR nathan@4: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL nathan@4: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR nathan@4: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER nathan@4: CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT nathan@4: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY nathan@4: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF nathan@4: SUCH DAMAGE. nathan@4: */ nathan@4: nathan@4: #define _LARGEFILE64_SOURCE nathan@4: #define _GNU_SOURCE nathan@4: nathan@4: #include nathan@4: #include nathan@4: #include nathan@4: #include nathan@4: #include nathan@4: #include nathan@4: #include nathan@4: #include nathan@4: nathan@4: #include "virtual.h" nathan@4: #include "cdbackup.h" nathan@4: #include "cdrom.h" nathan@4: #include "misc.h" nathan@4: #include "debug.h" nathan@4: nathan@4: int fd=-1; nathan@4: struct toc_entry *toc=0; nathan@4: long long cd_used, cd_avail; nathan@4: nathan@4: static struct cd_header cd_header; nathan@4: nathan@4: static unsigned char virt_buffer[VIRT_HEADER_LEN]; nathan@4: struct virt_header *virt_header=(struct virt_header *)virt_buffer; nathan@6: int virtualMissing=0, virt_off=-1, virt_regular=0; nathan@4: char *real_virt_name=0; nathan@4: nathan@4: extern int virtual; nathan@4: extern int disknum; nathan@4: extern char *virt_name, *cd_dev; nathan@4: nathan@4: /****************************************************************************/ nathan@4: nathan@4: void Vopen(int ro) nathan@4: { nathan@4: Vclose(); nathan@4: if(!virtual) { nathan@4: if((fd=open64(cd_dev,O_RDONLY|O_NONBLOCK))<0) nathan@4: error("Open failed (device)"); nathan@4: } nathan@4: else { nathan@4: free(real_virt_name); nathan@6: if(disknum==1 || !virt_regular) { nathan@4: real_virt_name=strdup(virt_name); nathan@4: } nathan@4: else if(virt_off>0) { nathan@4: char *strip=strdup(virt_name); nathan@4: char *dot=rindex(strip,'.'); nathan@4: if(dot) { nathan@4: *dot=0; nathan@13: if(asprintf(&real_virt_name,"%s.%d",strip,disknum+virt_off)<0) nathan@13: error("error making virtual name"); nathan@4: } nathan@4: else serror("Bad filename format"); nathan@4: free(strip); nathan@4: } nathan@4: else { nathan@13: if(asprintf(&real_virt_name,"%s.%d",virt_name,disknum)<0) nathan@13: error("error making virtual name"); nathan@4: } nathan@4: DEBUG("Vopen: real filename is '%s' disknum=%d virt_off=%d\n", nathan@4: real_virt_name,disknum,virt_off); nathan@4: virtualMissing=0; virt_regular=0; nathan@4: if((fd=open64(real_virt_name,ro ? O_RDONLY:O_RDWR))<0) { nathan@4: if(errno==EACCES || errno==ENOENT || errno==ENOTDIR) { nathan@4: virtualMissing=1; virt_regular=1; nathan@4: DEBUG("Vopen: missing virtual image, assuming an empty one\n"); nathan@4: } nathan@4: else error("Open failed (virtual)"); nathan@4: } nathan@4: else { nathan@4: struct stat64 st; nathan@4: if(fstat64(fd,&st)<0) error("Stat failed (virtual)"); nathan@4: if(S_ISREG(st.st_mode)) virt_regular=1; nathan@4: } nathan@4: } nathan@4: } nathan@4: nathan@4: /****************************************************************************/ nathan@4: nathan@4: void Vclose(void) nathan@4: { nathan@4: if(fd>=0) { nathan@4: close(fd); nathan@4: fd=-1; nathan@4: } nathan@4: } nathan@4: nathan@4: /****************************************************************************/ nathan@4: nathan@4: int VisRegular(void) nathan@4: { nathan@4: return virt_regular; nathan@4: } nathan@4: nathan@4: /****************************************************************************/ nathan@4: nathan@4: static int VgetCdHeader(struct cd_header *cd) nathan@4: { nathan@4: if(virtualMissing) { nathan@4: if(verbose) nathan@4: fprintf(stderr,"%s: Unable to get virtual image header, assuming new virtual image\n",prg_name); nathan@4: memset(virt_buffer,0,VIRT_HEADER_LEN); nathan@4: virt_header->magic=VIRT_MAGIC; nathan@4: virt_header->version=VIRT_VERSION; nathan@4: virt_header->leadout=1; nathan@4: virt_header->count=disknum + (virt_off>0 ? virt_off:0); nathan@4: } nathan@4: else { nathan@4: int n; nathan@4: if((n=read(fd,virt_buffer,VIRT_HEADER_LEN))<0) nathan@4: error("Read failed (virtual header)"); nathan@4: if(n!=VIRT_HEADER_LEN) nathan@4: serror("Short read on virtual header"); nathan@4: if(virt_header->magic!=VIRT_MAGIC) nathan@4: serror("Missing magic value in virtual header. Really a virtual image?"); nathan@4: if(virt_header->version>VIRT_VERSION) nathan@4: serror("Don't know how to handle this virtual image version"); nathan@4: } nathan@4: nathan@4: if(virt_off<0 && disknum==1) { nathan@4: virt_off=virt_header->count-1; nathan@4: DEBUG("VgetCdHeader: setting virt_off=%d\n",virt_off); nathan@4: } nathan@4: cd->start_track=1; cd->end_track=virt_header->tracks; nathan@4: cd->used=virt_header->leadout; nathan@4: return cd->end_track; nathan@4: } nathan@4: nathan@4: /****************************************************************************/ nathan@4: nathan@4: static void VgetCdTrack(int num, struct cd_track *cd) nathan@4: { nathan@4: cd->start_sec=virt_header->start[num-1]; nathan@4: cd->leadout_size=0; nathan@4: cd->is_data=1; nathan@4: } nathan@4: nathan@4: /****************************************************************************/ nathan@4: nathan@4: int VreadToc(int trackInfos) nathan@4: { nathan@4: struct cd_track cd_track; nathan@4: int i, tracks; nathan@4: nathan@4: tracks=virtual ? VgetCdHeader(&cd_header) : getCdHeader(&cd_header); nathan@4: if(!tracks) { nathan@4: cd_used=0; cd_avail=(long long)cd_len*CD_FRAMESIZE; nathan@4: DEBUG("Vreadtoc: empty media\n"); nathan@4: return 0; nathan@4: } nathan@4: DEBUG("Vreadtoc: starttrack=%d endtrack=%d tracks=%d\n", nathan@4: cd_header.start_track,cd_header.end_track,tracks); nathan@4: nathan@4: cd_used =(long long)cd_header.used*CD_FRAMESIZE; nathan@4: cd_avail =(long long)cd_len*CD_FRAMESIZE-cd_used; nathan@4: if(cd_avail<0) cd_avail=0; nathan@4: DEBUG("Vreadtoc: cd_used=%lld (%lld secs) cd_avail=%lld (%lld secs)\n", nathan@4: cd_used,cd_used/CD_FRAMESIZE,cd_avail,cd_avail/CD_FRAMESIZE); nathan@4: nathan@4: free(toc); nathan@4: if(!(toc=calloc(tracks,sizeof(struct toc_entry)))) serror("No memory for TOC"); nathan@4: nathan@4: cd_track.start_track=cd_header.start_track; nathan@4: for(i=tracks-1; i>=0; i--) { nathan@4: int t=cd_header.start_track+i; nathan@4: if(virtual) VgetCdTrack(t,&cd_track); else getCdTrack(t,&cd_track); nathan@4: toc[i].track_no=t; nathan@4: toc[i].sec_start=cd_track.start_sec; nathan@4: toc[i].sec_end=((i==tracks-1) ? cd_header.used : toc[i+1].sec_start)-1-cd_track.leadout_size; nathan@4: toc[i].is_data=cd_track.is_data; nathan@4: DEBUG("Vreadtoc: index=%d track=%d sec_start=%d sec_end=%d data=%d\n", nathan@4: i,t,toc[i].sec_start,toc[i].sec_end,toc[i].is_data); nathan@4: } nathan@4: nathan@4: if(trackInfos) { nathan@4: for(i=0; iid_str,strlen(SHORT_HDR))) { nathan@4: toc[i].is_cdbackup=1; nathan@4: strncpy(toc[i].id_str,track_header->id_str,32); toc[i].id_str[32]=0; nathan@4: strncpy(toc[i].vol_id, track_header->vol_id,32); toc[i].vol_id[32]=0; nathan@4: strncpy(toc[i].t_stamp, track_header->t_stamp,12); toc[i].t_stamp[12]=0; nathan@4: toc[i].disk_set = track_header->disk_set; nathan@4: toc[i].flags = track_header->flags; nathan@4: } nathan@4: } nathan@4: } nathan@4: } nathan@4: nathan@4: return tracks; nathan@4: } nathan@4: nathan@4: /****************************************************************************/ nathan@4: nathan@4: const char *VdevName(void) nathan@4: { nathan@4: static char buff[80]; nathan@4: if(virtual) snprintf(buff,sizeof(buff),"image %s",virt_name); nathan@4: else snprintf(buff,sizeof(buff),"device %s",cd_dev); nathan@4: return buff; nathan@4: } nathan@4: nathan@4: /****************************************************************************/ nathan@4: nathan@4: void VprintSpace(void) nathan@4: { nathan@4: if(verbose) { nathan@4: char flex1[16], flex2[16], flex3[16]; nathan@4: fprintf(stderr, nathan@4: "Disk size: %s (%7ld blocks)\n" nathan@4: "Space used: %s (%7lld blocks)\n" nathan@4: "Space avail:%s (%7lld blocks)\n", nathan@4: FlexSize(flex1,(long long)cd_len*CD_FRAMESIZE),cd_len, nathan@4: FlexSize(flex2,cd_used), cd_used/CD_FRAMESIZE, nathan@4: FlexSize(flex3,cd_avail), cd_avail/CD_FRAMESIZE); nathan@4: } nathan@4: } nathan@4: nathan@4: /****************************************************************************/ nathan@4: nathan@4: long long Vseek(int trackNum) nathan@4: { nathan@4: long long pos=0; nathan@4: if(trackNum>=0) pos=(long long)toc[trackNum].sec_start*CD_FRAMESIZE; nathan@4: DEBUG("Vseek: seeking to index %d, track %d, offset %lld (%s)\n", nathan@4: trackNum,toc[trackNum].track_no,pos,virtual ? "virtual":"real"); nathan@4: if(lseek64(fd,pos,SEEK_SET)<0) error("Seek failed"); nathan@4: return pos; nathan@4: } nathan@4: nathan@4: /****************************************************************************/ nathan@4: nathan@4: void Vread(void *buf) nathan@4: { nathan@4: if(full_read(fd,buf,CD_FRAMESIZE)!=CD_FRAMESIZE) nathan@4: serror("Unexpected EOF reading data"); nathan@4: } nathan@4: nathan@4: /****************************************************************************/ nathan@4: nathan@4: static unsigned long r_ahead, r_fahead; nathan@4: nathan@4: void VgetAhead(void) nathan@4: { nathan@4: if(!virtual) get_param(fd,&r_ahead,&r_fahead); nathan@4: } nathan@4: nathan@4: /****************************************************************************/ nathan@4: nathan@4: void VsetAhead(int restore) nathan@4: { nathan@4: if(!virtual) { nathan@4: if(restore) set_param(fd,r_ahead,r_fahead); nathan@4: else set_param(fd,0,0); nathan@4: } nathan@4: }