📄 growisofs_mmc.cpp
字号:
#ifndef _LARGEFILE_SOURCE#define _LARGEFILE_SOURCE#endif#ifndef _LARGEFILE64_SOURCE#define _LARGEFILE64_SOURCE#endif#ifndef _FILE_OFFSET_BITS#define _FILE_OFFSET_BITS 64#endif#if defined(__linux)/* ... and "engage" glibc large file support */#ifndef _GNU_SOURCE#define _GNU_SOURCE#endif#endif#include "transport.hxx"#include <time.h>#include <sys/types.h>#include <signal.h>#define sigs_mask(how) do { \ sigset_t mask; \ \ sigemptyset (&mask); \ sigaddset (&mask,SIGHUP), sigaddset (&mask,SIGINT), \ sigaddset (&mask,SIGTERM), sigaddset (&mask,SIGPIPE); \ \ sigprocmask (how,&mask,NULL); \} while (0)#define ONEX 1352 // 1385 * 1000 / 1024static int media_written=0,next_track=1,velocity=0, is_dao=0,quickgrown=0,do_reload=1;static void *ioctl_handle=(void *)-1;#ifndef ioctl_fd#define ioctl_fd ((long)ioctl_handle)#endifstatic unsigned int next_wr_addr=1; // it starts as booleanstatic unsigned int buf_size=256; // measured in 1KBsstatic unsigned char formats[260],disc_info[32];extern int dvd_compat,test_write,no_reload,mmc_profile,_argc,wrvfy;extern double speed_factor;extern char *ioctl_device,**_argv;extern "C"int fumount (int fd){ Scsi_Command cmd; return cmd.umount(fd);}extern "C"int media_reload (char *name=NULL,struct stat *sb=NULL){ if (name==NULL) { Scsi_Command cmd(ioctl_handle); while (1) // Pioneer DVR-x06 needs this... { cmd[0] = 0x1B; // START/STOP UNIT cmd[1] = 0x1; // "IMMED" cmd[4] = 0; // "Stop" cmd[5] = 0; if (cmd.transport() == 0x20407) // "OP IN PROGRESS" { poll (NULL,0,333); continue; } break; }#if defined(RELOAD_NEVER_NEEDED)#undef RELOAD_NEVER_NEEDED#define RELOAD_NEVER_NEEDED 1#else#define RELOAD_NEVER_NEEDED 0#endif if (RELOAD_NEVER_NEEDED || no_reload>0) { cmd[0] = 0x1E; // ALLOW MEDIA REMOVAL cmd[5] = 0; cmd.transport (); return (errno=0); }#if !RELOAD_NEVER_NEEDED char str[12]; int n; if ((n=fcntl (ioctl_fd,F_GETFD))<0) n=0; fcntl (ioctl_fd,F_SETFD,n&~FD_CLOEXEC); sprintf (str,"%d",ioctl_fd); execlp(_argv[0],"-reload",str,ioctl_device,NULL); } else { { Scsi_Command cmd; if (!cmd.associate (name,sb)) return 1; if (cmd.is_reload_needed()) { fprintf (stderr,"%s: reloading tray\n",name); cmd[0] = 0x1E; // ALLOW MEDIA REMOVAL cmd[5] = 0; if (cmd.transport ()) return 1; while (1) // Pioneer DVR-x05 needs this... { cmd[0] = 0x1B; // START/STOP UNIT cmd[1] = 0x1; // "IMMED" cmd[4] = 0x2; // "Eject" cmd[5] = 0; if (cmd.transport() == 0x20407) // "OP IN PROGRESS" { poll (NULL,0,333); continue; } break; } // yes, once again, non-"IMMED"... cmd[0] = 0x1B; // START/STOP UNIT cmd[4] = 0x2; // "Eject" cmd[5] = 0; if (cmd.transport()) return 1; }#if defined(__sun) || defined(sun) else if (volmgt_running()) { setuid(getuid()); execl("/usr/bin/volrmmount","volrmmount","-i",name,NULL); return 0; // not normally reached }#endif else return 0; // m-m-m-m! patched kernel:-) } if (no_reload>=0) { Scsi_Command cmd; if (cmd.associate (name,sb)) { cmd[0] = 0x1B; // START/STOP UNIT cmd[1] = 0x1; // "IMMED" cmd[4] = 0x3; // "Load" cmd[5] = 0; cmd.transport (); } errno=0; // ignore all errors on load } return 0;#endif } return 1;}extern "C"int get_mmc_profile (void *fd){ Scsi_Command cmd(fd); unsigned char buf[8],inq[128]; int profile=0,once=1,blank=0,err; unsigned int len; // INQUIRY is considered to be "non-intrusive" in a sense that // it won't interfere with any other operation nor clear sense // data, which might be important to retain for security reasons. cmd[0] = 0x12; // INQUIRY cmd[4] = 36; cmd[5] = 0; if ((err=cmd.transport(READ,inq,36))) sperror ("INQUIRY",err), exit (FATAL_START(errno)); // make sure we're talking to MMC unit, for security reasons... if ((inq[0]&0x1F) != 5) fprintf (stderr,":-( not an MMC unit!\n"), exit (FATAL_START(EINVAL)); do { cmd[0] = 0x46; cmd[8] = sizeof(buf); cmd[9] = 0; if ((err=cmd.transport(READ,buf,sizeof(buf)))) sperror ("GET CONFIGURATION",err), fprintf (stderr,":-( non-MMC unit?\n"), exit (FATAL_START(errno)); if ((profile = buf[6]<<8|buf[7]) || !once) break; // no media? cmd[0] = 0; // TEST UNIT READY cmd[5] = 0; if ((cmd.transport()&0xFFF00) != 0x23A00) break; // try to load tray... cmd[0] = 0x1B; // START/STOP UNIT cmd[4] = 0x3; // "Load" cmd[5] = 0; if ((err=cmd.transport ())) sperror ("LOAD TRAY",err), exit (FATAL_START(errno));#if 1 wait_for_unit (cmd);#else // consume sense data, most likely "MEDIA MAY HAVE CHANGED" cmd[0] = 0; // TEST UNIT READY cmd[5] = 0; if ((err=cmd.transport ()) == -1) sperror ("TEST UNIT READY",err), exit (FATAL_START(errno));#endif } while (once--); if (profile==0 || (profile&0x30)==0) // no or non-DVD media... return profile; cmd[0] = 0x51; // READ DISC INFORMATION cmd[8] = sizeof(disc_info); cmd[9] = 0; if ((err=cmd.transport (READ,disc_info,sizeof(disc_info)))) sperror ("READ DISC INFORMATION",err), exit (FATAL_START(errno)); // see if it's blank media if ((disc_info[2]&3) == 0) blank=0x10000; if (profile != 0x1A && profile != 0x13 && profile != 0x12) return blank|profile; cmd[0] = 0x23; // READ FORMAT CAPACITIES cmd[8] = 12; cmd[9] = 0; if ((err=cmd.transport (READ,formats,12))) sperror ("READ FORMAT CAPACITIES",err), exit (FATAL_START(errno)); len = formats[3]; if (len&7 || len<16) fprintf (stderr,":-( FORMAT allocaion length isn't sane"), exit (FATAL_START(EINVAL)); cmd[0] = 0x23; // READ FORMAT CAPACITIES cmd[7] = (4+len)>>8; cmd[8] = (4+len)&0xFF; cmd[9] = 0; if ((err=cmd.transport (READ,formats,4+len))) sperror ("READ FORMAT CAPACITIES",err), exit (FATAL_START(errno)); if (len != formats[3]) fprintf (stderr,":-( parameter length inconsistency\n"), exit(FATAL_START(EINVAL)); // see if it's not formatted if ((formats[8]&3) != 2) blank = 0x10000; return blank|profile;}static unsigned int get_2k_capacity (Scsi_Command &cmd){ unsigned char buf[32]; unsigned int ret=0; unsigned int nwa,free_blocks; int i,obligatory,len,err; obligatory=0x00; switch (mmc_profile&0xFFFF) { case 0x1A: // DVD+RW obligatory=0x26; case 0x13: // DVD-RW Restricted Overwrite for (i=8,len=formats[3];i<len;i+=8) if ((formats [4+i+4]>>2) == obligatory) break; if (i==len) { fprintf (stderr,":-( can't locate obligatory format descriptor\n"); return 0; } ret = formats[4+i+0]<<24; ret |= formats[4+i+1]<<16; ret |= formats[4+i+2]<<8; ret |= formats[4+i+3]; nwa = formats[4+5]<<16|formats[4+6]<<8|formats[4+7]; if (nwa>2048) ret *= nwa/2048; else if (nwa<2048) ret /= 2048/nwa; break; case 0x12: // DVD-RAM // As for the moment of this writing I don't format DVD-RAM. // Therefore I just pull formatted capacity for now... ret = formats[4+0]<<24; ret |= formats[4+1]<<16; ret |= formats[4+2]<<8; ret |= formats[4+3]; nwa = formats[4+5]<<16|formats[4+6]<<8|formats[4+7]; if (nwa>2048) ret *= nwa/2048; else if (nwa<2048) ret /= 2048/nwa; break; case 0x11: // DVD-R case 0x14: // DVD-RW Sequential case 0x1B: // DVD+R case 0x2B: // DVD+R Double Layer cmd[0] = 0x52; // READ TRACK INFORMATION cmd[1] = 1; cmd[4] = next_track>>8; cmd[5] = next_track&0xFF; // last track, set up earlier cmd[8] = sizeof(buf); cmd[9] = 0; if ((err=cmd.transport (READ,buf,sizeof(buf)))) { sperror ("READ TRACK INFORMATION",err); return 0; } nwa = 0; if (buf[7]&1) // NWA_V { nwa = buf[12]<<24; nwa |= buf[13]<<16; nwa |= buf[14]<<8; nwa |= buf[15]; } free_blocks = buf[16]<<24; free_blocks |= buf[17]<<16; free_blocks |= buf[18]<<8; free_blocks |= buf[19]; ret = nwa + free_blocks; break; default: break; } return ret;}extern "C"off64_t get_capacity (void *fd){ Scsi_Command cmd(fd); return (off64_t)get_2k_capacity(cmd)*2048;}ssize_t poor_mans_pwrite64 (int fd,const void *_buff,size_t size,off64_t foff){ Scsi_Command cmd(ioctl_handle); /* screw first argument */ unsigned char bcap[12]; const unsigned char *buff=(const unsigned char *)_buff; unsigned int lba,nbl,bsize,bfree; int retries=0,errcode; static int dao_toggle=-1; if (foff&0x7FFF || size&0x7FFF) // 32K block size return (errno=EINVAL,-1); lba = foff>>11; nbl = size>>11; if (!media_written && next_wr_addr) { if ((lba+nbl) <= next_wr_addr) return size; else if (next_wr_addr > lba) nbl -= (next_wr_addr-lba), size -= (next_wr_addr-lba)<<11, buff += (next_wr_addr-lba)<<11, lba = next_wr_addr; }#if defined(__sun) || defined(sun) else next_wr_addr = lba;#endif if (dao_toggle<0) dao_toggle=is_dao; { static unsigned int first_wr_addr=0; if (!media_written) { sigs_mask (SIG_BLOCK); first_wr_addr = lba; } else if (lba >= (first_wr_addr+buf_size/2)) // measured in 2KBs! { sigs_mask (SIG_UNBLOCK); } } while (1) { cmd[0] = wrvfy?0x2E:0x2A; // WRITE [AND VERIFY] (10) cmd[2] = (lba>>24)&0xff; // Logical Block Addrss cmd[3] = (lba>>16)&0xff; cmd[4] = (lba>>8)&0xff; cmd[5] = lba&0xff; cmd[7] = (nbl>>8)&0xff; cmd[8] = nbl&0xff; cmd[9] = 0;#if 0 cmd[0] = 0xAA; // WRITE(12) cmd[2] = (lba>>24)&0xff; // Logical Block Addrss cmd[3] = (lba>>16)&0xff; cmd[4] = (lba>>8)&0xff; cmd[5] = lba&0xff; cmd[8] = (nbl>>8)&0xff; cmd[9] = nbl&0xff; cmd[10] = 0x80; // "Streaming" cmd[11] = 0;#endif // // First writes can be long, especially in DAO mode... // I wish I could complement this with "if (lba==0)," // but some units might choose to fill the buffer before // they take the first nap... // cmd.timeout(dao_toggle?180:60); // // It should also be noted that under Linux these values // (if actually respected by kernel!) can turn out bogus. // The problem is that I scale them to milliseconds as // documentation requires/implies, while kernel treats // them as "jiffies." I could/should have used HZ macro // (or sysconf(_SC_CLK_TCK)), but recent kernels maintain // own higher HZ value and disrespects the user-land one. // Sending them down as milliseconds is just safer... // if (!(errcode=cmd.transport (WRITE,(void *)buff,size))) break; //--- WRITE failed ---//#if defined(__sun) || defined(sun) // // Solaris can slice USB WRITEs to multiple ones. Here I try // to find out which slice has failed. I expect we get here // only when we re-enter the loop... // if (lba==next_wr_addr && errcode==0x52102) // "INVALID ADDRESS FOR WRITE" { unsigned char track[32]; cmd[0] = 0x52; // READ TRACK INFORMATION cmd[1] = 1; cmd[4] = next_track>>8; cmd[5] = next_track&0xFF; cmd[8] = sizeof(track); cmd[9] = 0; if (!cmd.transport (READ,track,sizeof(track))) { if (track[7]&1) // NWA_V { next_wr_addr = track[12]<<24; next_wr_addr |= track[13]<<16; next_wr_addr |= track[14]<<8; next_wr_addr |= track[15]; if (lba<next_wr_addr && (lba+nbl)>next_wr_addr) { nbl -= next_wr_addr-lba, size -= (next_wr_addr-lba)<<11,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -