File: devices.c

package info (click to toggle)
jfsutils 1.0.14-1
  • links: PTS
  • area: main
  • in suites: woody
  • size: 3,132 kB
  • ctags: 4,667
  • sloc: ansic: 41,422; sh: 335; makefile: 126
file content (327 lines) | stat: -rwxr-xr-x 11,976 bytes parent folder | download
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, &sector_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;
}