| /* Copyright 1995-2007,2009,2011 Alain Knaff. |
| * This file is part of mtools. |
| * |
| * Mtools is free software: you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation, either version 3 of the License, or |
| * (at your option) any later version. |
| * |
| * Mtools is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with Mtools. If not, see <http://www.gnu.org/licenses/>. |
| * |
| * Io to a plain file or device |
| * |
| * written by: |
| * |
| * Alain L. Knaff |
| * alain@knaff.lu |
| * |
| */ |
| |
| #include "sysincludes.h" |
| #include "stream.h" |
| #include "mtools.h" |
| #include "msdos.h" |
| #include "plain_io.h" |
| #include "scsi.h" |
| #include "partition.h" |
| #include "llong.h" |
| |
| #ifdef HAVE_LINUX_FS_H |
| # include <linux/fs.h> |
| #endif |
| |
| typedef struct SimpleFile_t { |
| Class_t *Class; |
| int refs; |
| Stream_t *Next; |
| Stream_t *Buffer; |
| struct MT_STAT statbuf; |
| int fd; |
| mt_off_t offset; |
| mt_off_t lastwhere; |
| int seekable; |
| int privileged; |
| #ifdef OS_hpux |
| int size_limited; |
| #endif |
| int scsi_sector_size; |
| void *extra_data; /* extra system dependent information for scsi */ |
| int swap; /* do the word swapping */ |
| } SimpleFile_t; |
| |
| |
| #include "lockdev.h" |
| |
| typedef int (*iofn) (int, char *, int); |
| |
| |
| static void swap_buffer(char *buf, size_t len) |
| { |
| unsigned int i; |
| for (i=0; i<len; i+=2) { |
| char temp = buf[i]; |
| buf[i] = buf[i+1]; |
| buf[i+1] = temp; |
| } |
| } |
| |
| |
| static int file_io(Stream_t *Stream, char *buf, mt_off_t where, int len, |
| iofn io) |
| { |
| DeclareThis(SimpleFile_t); |
| int ret; |
| |
| where += This->offset; |
| |
| if (This->seekable && where != This->lastwhere ){ |
| if(mt_lseek( This->fd, where, SEEK_SET) < 0 ){ |
| perror("seek"); |
| This->lastwhere = (mt_off_t) -1; |
| return -1; |
| } |
| } |
| |
| #ifdef OS_hpux |
| /* |
| * On HP/UX, we can not write more than MAX_LEN bytes in one go. |
| * If more are written, the write fails with EINVAL |
| */ |
| #define MAX_SCSI_LEN (127*1024) |
| if(This->size_limited && len > MAX_SCSI_LEN) |
| len = MAX_SCSI_LEN; |
| #endif |
| ret = io(This->fd, buf, len); |
| |
| #ifdef OS_hpux |
| if (ret == -1 && |
| errno == EINVAL && /* if we got EINVAL */ |
| len > MAX_SCSI_LEN) { |
| This->size_limited = 1; |
| len = MAX_SCSI_LEN; |
| ret = io(This->fd, buf, len); |
| } |
| #endif |
| |
| if ( ret == -1 ){ |
| perror("plain_io"); |
| This->lastwhere = (mt_off_t) -1; |
| return -1; |
| } |
| This->lastwhere = where + ret; |
| return ret; |
| } |
| |
| |
| |
| static int file_read(Stream_t *Stream, char *buf, mt_off_t where, size_t len) |
| { |
| DeclareThis(SimpleFile_t); |
| |
| int result = file_io(Stream, buf, where, len, (iofn) read); |
| |
| if ( This->swap ) |
| swap_buffer( buf, len ); |
| return result; |
| } |
| |
| static int file_write(Stream_t *Stream, char *buf, mt_off_t where, size_t len) |
| { |
| DeclareThis(SimpleFile_t); |
| |
| if ( !This->swap ) |
| return file_io(Stream, buf, where, len, (iofn) write); |
| else { |
| int result; |
| char *swapping = malloc( len ); |
| memcpy( swapping, buf, len ); |
| swap_buffer( swapping, len ); |
| |
| result = file_io(Stream, swapping, where, len, (iofn) write); |
| |
| free(swapping); |
| return result; |
| } |
| } |
| |
| static int file_flush(Stream_t *Stream UNUSEDP) |
| { |
| #if 0 |
| DeclareThis(SimpleFile_t); |
| |
| return fsync(This->fd); |
| #endif |
| return 0; |
| } |
| |
| static int file_free(Stream_t *Stream) |
| { |
| DeclareThis(SimpleFile_t); |
| |
| if (This->fd > 2) |
| return close(This->fd); |
| else |
| return 0; |
| } |
| |
| static int file_geom(Stream_t *Stream, struct device *dev, |
| struct device *orig_dev, |
| int media, union bootsector *boot) |
| { |
| int ret; |
| DeclareThis(SimpleFile_t); |
| size_t tot_sectors; |
| int BootP, Infp0, InfpX, InfTm; |
| int sectors, j; |
| unsigned char sum; |
| int sect_per_track; |
| struct label_blk_t *labelBlock; |
| |
| dev->ssize = 2; /* allow for init_geom to change it */ |
| dev->use_2m = 0x80; /* disable 2m mode to begin */ |
| |
| if(media == 0xf0 || media >= 0x100){ |
| dev->heads = WORD(nheads); |
| dev->sectors = WORD(nsect); |
| tot_sectors = DWORD(bigsect); |
| SET_INT(tot_sectors, WORD(psect)); |
| sect_per_track = dev->heads * dev->sectors; |
| if(sect_per_track == 0) { |
| if(mtools_skip_check) { |
| /* add some fake values if sect_per_track is |
| * zero. Indeed, some atari disks lack the |
| * geometry values (i.e. have zeroes in their |
| * place). In order to avoid division by zero |
| * errors later on, plug 1 everywhere |
| */ |
| dev->heads = 1; |
| dev->sectors = 1; |
| sect_per_track = 1; |
| } else { |
| fprintf(stderr, "The devil is in the details: zero number of heads or sectors\n"); |
| exit(1); |
| } |
| } |
| tot_sectors += sect_per_track - 1; /* round size up */ |
| dev->tracks = tot_sectors / sect_per_track; |
| |
| BootP = WORD(ext.old.BootP); |
| Infp0 = WORD(ext.old.Infp0); |
| InfpX = WORD(ext.old.InfpX); |
| InfTm = WORD(ext.old.InfTm); |
| |
| if(WORD(fatlen)) { |
| labelBlock = &boot->boot.ext.old.labelBlock; |
| } else { |
| labelBlock = &boot->boot.ext.fat32.labelBlock; |
| } |
| |
| if (boot->boot.descr >= 0xf0 && |
| has_BPB4 && |
| strncmp( boot->boot.banner,"2M", 2 ) == 0 && |
| BootP < 512 && Infp0 < 512 && InfpX < 512 && InfTm < 512 && |
| BootP >= InfTm + 2 && InfTm >= InfpX && InfpX >= Infp0 && |
| Infp0 >= 76 ){ |
| for (sum=0, j=63; j < BootP; j++) |
| sum += boot->bytes[j];/* checksum */ |
| dev->ssize = boot->bytes[InfTm]; |
| if (!sum && dev->ssize <= 7){ |
| dev->use_2m = 0xff; |
| dev->ssize |= 0x80; /* is set */ |
| } |
| } |
| } else |
| if(setDeviceFromOldDos(media, dev) < 0) |
| exit(1); |
| |
| sectors = dev->sectors; |
| dev->sectors = dev->sectors * WORD(secsiz) / 512; |
| |
| #ifdef JPD |
| printf("file_geom:media=%0X=>cyl=%d,heads=%d,sects=%d,ssize=%d,use2m=%X\n", |
| media, dev->tracks, dev->heads, dev->sectors, dev->ssize, |
| dev->use_2m); |
| #endif |
| ret = init_geom(This->fd,dev, orig_dev, &This->statbuf); |
| dev->sectors = sectors; |
| #ifdef JPD |
| printf("f_geom: after init_geom(), sects=%d\n", dev->sectors); |
| #endif |
| return ret; |
| } |
| |
| |
| static int file_data(Stream_t *Stream, time_t *date, mt_size_t *size, |
| int *type, int *address) |
| { |
| DeclareThis(SimpleFile_t); |
| |
| if(date) |
| *date = This->statbuf.st_mtime; |
| if(size) |
| *size = This->statbuf.st_size; |
| if(type) |
| *type = S_ISDIR(This->statbuf.st_mode); |
| if(address) |
| *address = 0; |
| return 0; |
| } |
| |
| static int file_discard(Stream_t *Stream) |
| { |
| #ifdef BLKFLSBUF |
| int ret; |
| DeclareThis(SimpleFile_t); |
| ret= ioctl(This->fd, BLKFLSBUF); |
| if(ret < 0) |
| perror("BLKFLSBUF"); |
| return ret; |
| #else |
| return 0; |
| #endif |
| } |
| |
| /* ZIP or other scsi device on Solaris or SunOS system. |
| Since Sun won't accept a non-Sun label on a scsi disk, we must |
| bypass Sun's disk interface and use low-level SCSI commands to read |
| or write the ZIP drive. We thus replace the file_read and file_write |
| routines with our own scsi_read and scsi_write routines, that use the |
| uscsi ioctl interface. By James Dugal, jpd@usl.edu, 11-96. Tested |
| under Solaris 2.5 and SunOS 4.3.1_u1 using GCC. |
| |
| Note: the mtools.conf entry for a ZIP drive would look like this: |
| (solaris) drive C: file="/dev/rdsk/c0t5d0s2" partition=4 FAT=16 nodelay exclusive scsi=& |
| (sunos) drive C: file="/dev/rsd5c" partition=4 FAT=16 nodelay exclusive scsi=1 |
| |
| Note 2: Sol 2.5 wants mtools to be suid-root, to use the ioctl. SunOS is |
| happy if we just have access to the device, so making mtools sgid to a |
| group called, say, "ziprw" which has rw permission on /dev/rsd5c, is fine. |
| */ |
| |
| static void scsi_init(SimpleFile_t *This) |
| { |
| int fd = This->fd; |
| unsigned char cdb[10],buf[8]; |
| |
| memset(cdb, 0, sizeof cdb); |
| memset(buf,0, sizeof(buf)); |
| cdb[0]=SCSI_READ_CAPACITY; |
| if (scsi_cmd(fd, (unsigned char *)cdb, |
| sizeof(cdb), SCSI_IO_READ, buf, sizeof(buf), This->extra_data)==0) |
| { |
| This->scsi_sector_size= |
| ((unsigned)buf[5]<<16)|((unsigned)buf[6]<<8)|(unsigned)buf[7]; |
| if (This->scsi_sector_size != 512) |
| fprintf(stderr," (scsi_sector_size=%d)\n",This->scsi_sector_size); |
| } |
| } |
| |
| static int scsi_io(Stream_t *Stream, char *buf, |
| mt_off_t where, size_t len, int rwcmd) |
| { |
| unsigned int firstblock, nsect; |
| int clen,r; |
| size_t max; |
| off_t offset; |
| unsigned char cdb[10]; |
| DeclareThis(SimpleFile_t); |
| |
| firstblock=truncBytes32((where + This->offset)/This->scsi_sector_size); |
| /* 512,1024,2048,... bytes/sector supported */ |
| offset=truncBytes32(where + This->offset - |
| firstblock*This->scsi_sector_size); |
| nsect=(offset+len+This->scsi_sector_size-1)/ This->scsi_sector_size; |
| #if defined(OS_sun) && defined(OS_i386) |
| if (This->scsi_sector_size>512) |
| firstblock*=This->scsi_sector_size/512; /* work around a uscsi bug */ |
| #endif /* sun && i386 */ |
| |
| if (len>512) { |
| /* avoid buffer overruns. The transfer MUST be smaller or |
| * equal to the requested size! */ |
| while (nsect*This->scsi_sector_size>len) |
| --nsect; |
| if(!nsect) { |
| fprintf(stderr,"Scsi buffer too small\n"); |
| exit(1); |
| } |
| if(rwcmd == SCSI_IO_WRITE && offset) { |
| /* there seems to be no memmove before a write */ |
| fprintf(stderr,"Unaligned write\n"); |
| exit(1); |
| } |
| /* a better implementation should use bounce buffers. |
| * However, in normal operation no buffer overruns or |
| * unaligned writes should happen anyways, as the logical |
| * sector size is (hopefully!) equal to the physical one |
| */ |
| } |
| |
| |
| max = scsi_max_length(); |
| |
| if (nsect > max) |
| nsect=max; |
| |
| /* set up SCSI READ/WRITE command */ |
| memset(cdb, 0, sizeof cdb); |
| |
| switch(rwcmd) { |
| case SCSI_IO_READ: |
| cdb[0] = SCSI_READ; |
| break; |
| case SCSI_IO_WRITE: |
| cdb[0] = SCSI_WRITE; |
| break; |
| } |
| |
| cdb[1] = 0; |
| |
| if (firstblock > 0x1fffff || nsect > 0xff) { |
| /* I suspect that the ZIP drive also understands Group 1 |
| * commands. If that is indeed true, we may chose Group 1 |
| * more aggressively in the future */ |
| |
| cdb[0] |= SCSI_GROUP1; |
| clen=10; /* SCSI Group 1 cmd */ |
| |
| /* this is one of the rare case where explicit coding is |
| * more portable than macros... The meaning of scsi command |
| * bytes is standardised, whereas the preprocessor macros |
| * handling it might be not... */ |
| |
| cdb[2] = (unsigned char) (firstblock >> 24) & 0xff; |
| cdb[3] = (unsigned char) (firstblock >> 16) & 0xff; |
| cdb[4] = (unsigned char) (firstblock >> 8) & 0xff; |
| cdb[5] = (unsigned char) firstblock & 0xff; |
| cdb[6] = 0; |
| cdb[7] = (unsigned char) (nsect >> 8) & 0xff; |
| cdb[8] = (unsigned char) nsect & 0xff; |
| cdb[9] = 0; |
| } else { |
| clen = 6; /* SCSI Group 0 cmd */ |
| cdb[1] |= (unsigned char) ((firstblock >> 16) & 0x1f); |
| cdb[2] = (unsigned char) ((firstblock >> 8) & 0xff); |
| cdb[3] = (unsigned char) firstblock & 0xff; |
| cdb[4] = (unsigned char) nsect; |
| cdb[5] = 0; |
| } |
| |
| if(This->privileged) |
| reclaim_privs(); |
| |
| r=scsi_cmd(This->fd, (unsigned char *)cdb, clen, rwcmd, buf, |
| nsect*This->scsi_sector_size, This->extra_data); |
| |
| if(This->privileged) |
| drop_privs(); |
| |
| if(r) { |
| perror(rwcmd == SCSI_IO_READ ? "SCMD_READ" : "SCMD_WRITE"); |
| return -1; |
| } |
| #ifdef JPD |
| printf("finished %u for %u\n", firstblock, nsect); |
| #endif |
| |
| #ifdef JPD |
| printf("zip: read or write OK\n"); |
| #endif |
| if (offset>0) memmove(buf,buf+offset,nsect*This->scsi_sector_size-offset); |
| if (len==256) return 256; |
| else if (len==512) return 512; |
| else return nsect*This->scsi_sector_size-offset; |
| } |
| |
| static int scsi_read(Stream_t *Stream, char *buf, mt_off_t where, size_t len) |
| { |
| |
| #ifdef JPD |
| printf("zip: to read %d bytes at %d\n", len, where); |
| #endif |
| return scsi_io(Stream, buf, where, len, SCSI_IO_READ); |
| } |
| |
| static int scsi_write(Stream_t *Stream, char *buf, mt_off_t where, size_t len) |
| { |
| #ifdef JPD |
| Printf("zip: to write %d bytes at %d\n", len, where); |
| #endif |
| return scsi_io(Stream, buf, where, len, SCSI_IO_WRITE); |
| } |
| |
| static Class_t ScsiClass = { |
| scsi_read, |
| scsi_write, |
| file_flush, |
| file_free, |
| file_geom, |
| file_data, |
| 0, /* pre-allocate */ |
| 0, /* dos-convert */ |
| file_discard |
| }; |
| |
| |
| static Class_t SimpleFileClass = { |
| file_read, |
| file_write, |
| file_flush, |
| file_free, |
| file_geom, |
| file_data, |
| 0, /* pre_allocate */ |
| 0, /* dos-convert */ |
| file_discard |
| }; |
| |
| |
| Stream_t *SimpleFileOpen(struct device *dev, struct device *orig_dev, |
| const char *name, int mode, char *errmsg, |
| int mode2, int locked, mt_size_t *maxSize) |
| { |
| SimpleFile_t *This; |
| #ifdef __EMX__ |
| HFILE FileHandle; |
| ULONG Action; |
| APIRET rc; |
| #endif |
| This = New(SimpleFile_t); |
| if (!This){ |
| printOom(); |
| return 0; |
| } |
| memset((void*)This, 0, sizeof(SimpleFile_t)); |
| This->scsi_sector_size = 512; |
| This->seekable = 1; |
| #ifdef OS_hpux |
| This->size_limited = 0; |
| #endif |
| This->Class = &SimpleFileClass; |
| if (!name || strcmp(name,"-") == 0 ){ |
| if (mode == O_RDONLY) |
| This->fd = 0; |
| else |
| This->fd = 1; |
| This->seekable = 0; |
| This->refs = 1; |
| This->Next = 0; |
| This->Buffer = 0; |
| if (MT_FSTAT(This->fd, &This->statbuf) < 0) { |
| Free(This); |
| if(errmsg) |
| #ifdef HAVE_SNPRINTF |
| snprintf(errmsg,199,"Can't stat -: %s", |
| strerror(errno)); |
| #else |
| sprintf(errmsg,"Can't stat -: %s", |
| strerror(errno)); |
| #endif |
| return NULL; |
| } |
| |
| return (Stream_t *) This; |
| } |
| |
| |
| if(dev) { |
| if(!(mode2 & NO_PRIV)) |
| This->privileged = IS_PRIVILEGED(dev); |
| mode |= dev->mode; |
| } |
| |
| precmd(dev); |
| if(IS_PRIVILEGED(dev) && !(mode2 & NO_PRIV)) |
| reclaim_privs(); |
| |
| #ifdef __EMX__ |
| #define DOSOPEN_FLAGS (OPEN_FLAGS_DASD | OPEN_FLAGS_WRITE_THROUGH | \ |
| OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_RANDOM | \ |
| OPEN_FLAGS_NO_CACHE) |
| #define DOSOPEN_FD_ACCESS (OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE) |
| #define DOSOPEN_HD_ACCESS (OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY) |
| |
| if (isalpha(*name) && (*(name+1) == ':')) { |
| rc = DosOpen( |
| name, &FileHandle, &Action, 0L, FILE_NORMAL, |
| OPEN_ACTION_OPEN_IF_EXISTS, DOSOPEN_FLAGS | |
| (IS_NOLOCK(dev)?DOSOPEN_HD_ACCESS:DOSOPEN_FD_ACCESS), |
| 0L); |
| #if DEBUG |
| if (rc != NO_ERROR) fprintf (stderr, "DosOpen() returned %d\n", rc); |
| #endif |
| if (!IS_NOLOCK(dev)) { |
| rc = DosDevIOCtl( |
| FileHandle, 0x08L, DSK_LOCKDRIVE, 0, 0, 0, 0, 0, 0); |
| #if DEBUG |
| if (rc != NO_ERROR) fprintf (stderr, "DosDevIOCtl() returned %d\n", rc); |
| #endif |
| } |
| if (rc == NO_ERROR) |
| This->fd = _imphandle(FileHandle); else This->fd = -1; |
| } else |
| #endif |
| { |
| if (IS_SCSI(dev)) |
| This->fd = scsi_open(name, mode, IS_NOLOCK(dev)?0444:0666, |
| &This->extra_data); |
| else |
| This->fd = open(name, mode | O_LARGEFILE | O_BINARY, |
| IS_NOLOCK(dev)?0444:0666); |
| } |
| |
| if(IS_PRIVILEGED(dev) && !(mode2 & NO_PRIV)) |
| drop_privs(); |
| |
| if (This->fd < 0) { |
| Free(This); |
| if(errmsg) |
| #ifdef HAVE_SNPRINTF |
| snprintf(errmsg, 199, "Can't open %s: %s", |
| name, strerror(errno)); |
| #else |
| sprintf(errmsg, "Can't open %s: %s", |
| name, strerror(errno)); |
| #endif |
| return NULL; |
| } |
| |
| if(IS_PRIVILEGED(dev) && !(mode2 & NO_PRIV)) |
| closeExec(This->fd); |
| |
| #ifdef __EMX__ |
| if (*(name+1) != ':') |
| #endif |
| if (MT_FSTAT(This->fd, &This->statbuf) < 0 |
| #ifdef OS_mingw32msvc |
| && strncmp(name, "\\\\.\\", 4) != 0 |
| #endif |
| ) { |
| Free(This); |
| if(errmsg) { |
| #ifdef HAVE_SNPRINTF |
| snprintf(errmsg,199,"Can't stat %s: %s", |
| name, strerror(errno)); |
| #else |
| if(strlen(name) > 50) { |
| sprintf(errmsg,"Can't stat file: %s", |
| strerror(errno)); |
| } else { |
| sprintf(errmsg,"Can't stat %s: %s", |
| name, strerror(errno)); |
| } |
| #endif |
| } |
| return NULL; |
| } |
| #ifndef __EMX__ |
| #ifndef __CYGWIN__ |
| #ifndef OS_mingw32msvc |
| /* lock the device on writes */ |
| if (locked && lock_dev(This->fd, mode == O_RDWR, dev)) { |
| if(errmsg) |
| #ifdef HAVE_SNPRINTF |
| snprintf(errmsg,199, |
| "plain floppy: device \"%s\" busy (%s):", |
| dev ? dev->name : "unknown", strerror(errno)); |
| #else |
| sprintf(errmsg, |
| "plain floppy: device \"%s\" busy (%s):", |
| (dev && strlen(dev->name) < 50) ? |
| dev->name : "unknown", strerror(errno)); |
| #endif |
| |
| if(errno != EOPNOTSUPP || mode == O_RDWR) { |
| /* If error is "not supported", and we're only |
| * reading from the device anyways, then ignore. Some |
| * OS'es don't support locks on read-only devices, even |
| * if they are shared (read-only) locks */ |
| close(This->fd); |
| Free(This); |
| return NULL; |
| } |
| } |
| #endif |
| #endif |
| #endif |
| /* set default parameters, if needed */ |
| if (dev){ |
| if ((!IS_MFORMAT_ONLY(dev) && dev->tracks) && |
| init_geom(This->fd, dev, orig_dev, &This->statbuf)){ |
| close(This->fd); |
| Free(This); |
| if(errmsg) |
| sprintf(errmsg,"init: set default params"); |
| return NULL; |
| } |
| This->offset = (mt_off_t) dev->offset; |
| } else |
| This->offset = 0; |
| |
| This->refs = 1; |
| This->Next = 0; |
| This->Buffer = 0; |
| |
| if(maxSize) { |
| if (IS_SCSI(dev)) { |
| *maxSize = MAX_OFF_T_B(31+log_2(This->scsi_sector_size)); |
| } else { |
| *maxSize = max_off_t_seek; |
| } |
| if(This->offset > (mt_off_t) *maxSize) { |
| close(This->fd); |
| Free(This); |
| if(errmsg) |
| sprintf(errmsg,"init: Big disks not supported"); |
| return NULL; |
| } |
| |
| *maxSize -= This->offset; |
| } |
| /* partitioned drive */ |
| |
| /* jpd@usl.edu: assume a partitioned drive on these 2 systems is a ZIP*/ |
| /* or similar drive that must be accessed by low-level scsi commands */ |
| /* AK: introduce new "scsi=1" statement to specifically set |
| * this option. Indeed, there could conceivably be partitioned |
| * devices where low level scsi commands will not be needed */ |
| if(IS_SCSI(dev)) { |
| This->Class = &ScsiClass; |
| if(This->privileged) |
| reclaim_privs(); |
| scsi_init(This); |
| if(This->privileged) |
| drop_privs(); |
| } |
| |
| This->swap = DO_SWAP( dev ); |
| |
| if(!(mode2 & NO_OFFSET) && |
| dev && (dev->partition > 4)) |
| fprintf(stderr, |
| "Invalid partition %d (must be between 0 and 4), ignoring it\n", |
| dev->partition); |
| |
| while(!(mode2 & NO_OFFSET) && |
| dev && dev->partition && dev->partition <= 4) { |
| int has_activated; |
| unsigned int last_end, j; |
| unsigned char buf[2048]; |
| struct partition *partTable=(struct partition *)(buf+ 0x1ae); |
| size_t partOff; |
| |
| /* read the first sector, or part of it */ |
| if (force_read((Stream_t *)This, (char*) buf, 0, 512) != 512) |
| break; |
| if( _WORD(buf+510) != 0xaa55) |
| break; |
| |
| partOff = BEGIN(partTable[dev->partition]); |
| if (maxSize) { |
| if (partOff > *maxSize >> 9) { |
| close(This->fd); |
| Free(This); |
| if(errmsg) |
| sprintf(errmsg,"init: Big disks not supported"); |
| return NULL; |
| } |
| *maxSize -= (mt_off_t) partOff << 9; |
| } |
| |
| This->offset += (mt_off_t) partOff << 9; |
| if(!partTable[dev->partition].sys_ind) { |
| if(errmsg) |
| sprintf(errmsg, |
| "init: non-existant partition"); |
| close(This->fd); |
| Free(This); |
| return NULL; |
| } |
| |
| if(!dev->tracks) { |
| dev->heads = head(partTable[dev->partition].end)+1; |
| dev->sectors = sector(partTable[dev->partition].end); |
| dev->tracks = cyl(partTable[dev->partition].end) - |
| cyl(partTable[dev->partition].start)+1; |
| } |
| dev->hidden= |
| dev->sectors*head(partTable[dev->partition].start) + |
| sector(partTable[dev->partition].start)-1; |
| if(!mtools_skip_check && |
| consistencyCheck((struct partition *)(buf+0x1ae), 0, 0, |
| &has_activated, &last_end, &j, dev, 0)) { |
| fprintf(stderr, |
| "Warning: inconsistent partition table\n"); |
| fprintf(stderr, |
| "Possibly unpartitioned device\n"); |
| fprintf(stderr, |
| "\n*** Maybe try without partition=%d in " |
| "device definition ***\n\n", |
| dev->partition); |
| fprintf(stderr, |
| "If this is a PCMCIA card, or a disk " |
| "partitioned on another computer, this " |
| "message may be in error: add " |
| "mtools_skip_check=1 to your .mtoolsrc " |
| "file to suppress this warning\n"); |
| |
| } |
| break; |
| /* NOTREACHED */ |
| } |
| |
| This->lastwhere = -This->offset; |
| /* provoke a seek on those devices that don't start on a partition |
| * boundary */ |
| |
| return (Stream_t *) This; |
| } |
| |
| int get_fd(Stream_t *Stream) |
| { |
| Class_t *clazz; |
| DeclareThis(SimpleFile_t); |
| clazz = This->Class; |
| if(clazz != &ScsiClass && |
| clazz != &SimpleFileClass) |
| return -1; |
| else |
| return This->fd; |
| } |
| |
| void *get_extra_data(Stream_t *Stream) |
| { |
| DeclareThis(SimpleFile_t); |
| |
| return This->extra_data; |
| } |