1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
|
/*
* Copyright (c) International Business Machines Corp., 2000
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include "jfs_types.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "devices.h"
#include "debug.h"
#include <sys/ioctl.h>
#if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE)
#define BLKGETSIZE _IO(0x12,96) /* return device size (sectors) */
#endif
#if defined(__linux__) && defined(_IO) && !defined(BLKSSZGET)
#define BLKSSZGET _IO(0x12,104) /* return sector size (bytes) */
#endif
/*
* NAME: ujfs_get_dev_size
*
* FUNCTION: Uses the device driver interface to determine the raw capacity of
* the specified device.
*
* PRE CONDITIONS:
*
* POST CONDITIONS:
*
* PARAMETERS:
* device - device
* size - filled in with size of device; not modified if failure occurs
*
* NOTES:
*
* DATA STRUCTURES:
*
* RETURNS: 0 if successful; anything else indicates failures
*/
int32_t ujfs_get_dev_size( HFILE device, int64_t *size)
{
off_t Starting_Position; /* position within file/device upon entry to this function. */
off_t Current_Position = 16777215; /* position we are attempting to read from. */
off_t Last_Valid_Position = 0; /* Last position we could successfully read from. */
off_t First_Invalid_Position; /* first invalid position we attempted to read from/seek to. */
off_t Seek_Result; /* value returned by lseek. */
size_t Read_Result = 0; /* value returned by read. */
char Test_Byte; /* used to validate a position that we successfully lseek'ed to. */
#ifdef BLKGETSIZE
#ifdef BLKSSZGET
unsigned long num_sectors = 0;
unsigned long sector_size = 0;
if (ioctl(device, BLKGETSIZE, &num_sectors) >= 0) {
if (ioctl(device, BLKSSZGET, §or_size) >= 0) {
/* for now, keep size as multiple of 1024,
not 512, so eliminate any odd sector. */
*size = (int64_t)sector_size * (int64_t)((num_sectors/2)*2);
return NO_ERROR;
}
}
#endif
#endif
/* If the ioctl above fails or is undefined, use a binary search to find the last
byte in the partition. This works because an lseek to a position within the
partition does not return an error while an lseek to a position beyond the end
of the partition does. Note that some SCSI drivers may log an 'access beyond
end of device' error message. */
/* Save the starting position so that we can restore it when we are done! */
Starting_Position = lseek(device,0,SEEK_CUR);
if ( Starting_Position < 0 )
return ERROR_SEEK;
/* Find a position beyond the end of the partition. We will start by attempting to seek to and read the
16777216th byte in the partition. We start here because a JFS partition must be at least this big. If
it is not, then we can not format it as JFS. */
do {
/* Seek to the location we wish to test. */
Seek_Result = lseek(device,Current_Position,SEEK_SET);
if ( Seek_Result == Current_Position ) {
/* Can we read from this location? */
Read_Result = read(device,&Test_Byte,1);
if ( Read_Result == 1 ) {
/* The current test position is valid. Save it for future reference. */
Last_Valid_Position = Current_Position;
/* Lets calculate the next location to test. */
Current_Position = ( ( Current_Position + 1 ) * 2 ) - 1;
}
}
} while ( ( Seek_Result == Last_Valid_Position ) && ( Read_Result == 1) );
/* We have exited the while loop, which means that Current Position is beyond the end of the partition
or is unreadable due to a hardware problem (bad block). Since the odds of hitting a bad block are very
low, we will ignore that condition for now. If time becomes available, then this issue can be revisited. */
/* Is the drive greater than 16MB? */
if ( Last_Valid_Position == 0 ) {
/* Determine if drive is readable at all. If it is, the drive is too
small. If not, it could be a newly created partion, so we need to
issue a different error message */
*size = 0; /* Indicates not readable at all */
Seek_Result = lseek(device,Last_Valid_Position,SEEK_SET); /* zero */
if ( Seek_Result == Last_Valid_Position ) {
/* Can we read from this location? */
Read_Result = read(device,&Test_Byte,1);
if ( Read_Result == 1 )
*size = 1; /* non-zero indicates readable, but too small */
}
} else {
/* The drive is larger than 16MB. Now we must find out exactly how large. */
/* We now have a point within the partition and one beyond it. The end of the partition must
lie between the two. We will use a binary search to find it. */
/* Setup for the binary search. */
First_Invalid_Position = Current_Position;
Current_Position = Last_Valid_Position + ( ( Current_Position - Last_Valid_Position ) / 2);
/* Iterate until the difference between the last valid position and the first invalid position is 2 or less. */
while ( ( First_Invalid_Position - Last_Valid_Position ) > 2 ) {
/* Seek to the location we wish to test. */
Seek_Result = lseek(device,Current_Position,SEEK_SET);
if ( Seek_Result == Current_Position ) {
/* Can we read from this location? */
Read_Result = read(device,&Test_Byte,1);
if ( Read_Result == 1) {
/* The current test position is valid. Save it for future reference. */
Last_Valid_Position = Current_Position;
/* Lets calculate the next location to test. It should be half way between the current
test position and the first invalid position that we know of. */
Current_Position = Current_Position + ( ( First_Invalid_Position - Last_Valid_Position ) / 2);
}
} else
Read_Result = 0;
if ( Read_Result != 1 ) {
/* Out test position is beyond the end of the partition. It becomes our first known invalid position. */
First_Invalid_Position = Current_Position;
/* Our new test position should be half way between our last known valid position and our current test position. */
Current_Position = Last_Valid_Position + ( ( Current_Position - Last_Valid_Position ) / 2);
}
}
/* The size of the drive should be Last_Valid_Position + 1 as Last_Valid_Position is an offset from the beginning of the partition. */
*size = Last_Valid_Position + 1;
}
/* Restore the original position. */
if ( Starting_Position != lseek(device,Starting_Position,SEEK_SET) )
return ERROR_SEEK;
return NO_ERROR;
}
/*
* NAME: ujfs_open_device
*
* FUNCTION: Open the specified device and return the handle and the
* struct DPB information.
*
* PRE CONDITIONS:
*
* POST CONDITIONS:
*
* PARAMETERS:
* Device - name of device to open
* FileHandle - Filled in with handle specified device
* SectorSize - Filled in with sector size of specified device; not
* modified if failure occurs
* mode - Indicates to open read-only, or read-write exclusive
*
* NOTES:
*
* DATA STRUCTURES:
*
* RETURNS: 0 for success; anything else indicates a failure
*/
int32_t ujfs_open_device( char *Device,
PHFILE FileHandle,
int32_t *SectorSize,
int32_t mode )
{
/* Open the device. */
switch ( mode ) {
case RDWR_EXCL :
*FileHandle = open(Device,O_RDWR | O_EXCL,0); /* Open the device with exclusive access in read/write mode. */
break;
case READONLY :
*FileHandle = open(Device,O_RDONLY,0); /* Open the device in read only mode. */
break;
default: return ERROR_INVALID_FUNCTION;
break;
}
if ( *FileHandle < 0 )
return ERROR_FILE_NOT_FOUND;
/* We need to get the sector size of the device. I am not sure this is necessary at the current time. */
*SectorSize = 512;
return NO_ERROR;
}
/*
* NAME: ujfs_rw_diskblocks
*
* FUNCTION: Read/Write specific number of bytes for an opened device.
*
* PRE CONDITIONS:
*
* POST CONDITIONS:
*
* PARAMETERS:
* dev_ptr - file handle of an opened device to read/write
* disk_offset - byte offset from beginning of device for start of disk
* block read/write
* disk_count - number of bytes to read/write
* data_buffer - On read this will be filled in with data read from
* disk; on write this contains data to be written
* mode - GET: read; PUT: write; VRFY: verify
*
* NOTES: A disk address is formed by {#cylinder, #head, #sector}
* In order to call this routine, the device must be opened by
* calling ujfs_open_device() so that the static global Datap
* and ptrklay are properly inited.
*
* Also the DSK_READTRACK and DSK_WRITETRACK is a track based
* function. If it needs to read/write crossing track boundary,
* additional calls are used.
*
* DATA STRUCTURES:
*
* RETURNS:
*/
int32_t ujfs_rw_diskblocks( HFILE dev_ptr,
int64_t disk_offset,
int32_t disk_count,
void *data_buffer,
int32_t mode )
{
off_t Actual_Location;
size_t Bytes_Transferred;
Actual_Location = lseek(dev_ptr,disk_offset, SEEK_SET);
if ( ( Actual_Location < 0 ) || ( Actual_Location != disk_offset ) )
return ERROR_SEEK;
switch ( mode ) {
case GET:
Bytes_Transferred = read(dev_ptr,data_buffer,disk_count);
break;
case PUT:
Bytes_Transferred = write(dev_ptr,data_buffer,disk_count);
break;
default:
DBG_ERROR(("Internal error: %s(%d): bad mode: %d\n", __FILE__,
__LINE__, mode))
return ERROR_INVALID_HANDLE;
break; /* Keep the compiler happy. */
}
if ( Bytes_Transferred != disk_count ) {
if ( mode == GET )
return ERROR_READ_FAULT;
else
return ERROR_WRITE_FAULT;
}
return NO_ERROR;
}
/*
* NAME: ujfs_close
*
* FUNCTION: Close the specified device and free the space for
* track layout table.
*
* PRE CONDITIONS:
*
* POST CONDITIONS:
*
* PARAMETERS:
* Device - File handle of the opened device
*
* NOTES:
*
* DATA STRUCTURES:
*
* RETURNS: 0 for success; anything else indicates a failure
*/
int32_t ujfs_close( HFILE device)
{
if ( close(device) )
return ERROR_INVALID_HANDLE;
return NO_ERROR;
}
|