|
|
1.1 root 1: /*
2: * Copyright (c) 1998-2000 Apple Computer, Inc. All rights reserved.
3: *
4: * @APPLE_LICENSE_HEADER_START@
5: *
6: * The contents of this file constitute Original Code as defined in and
7: * are subject to the Apple Public Source License Version 1.1 (the
8: * "License"). You may not use this file except in compliance with the
9: * License. Please obtain a copy of the License at
10: * http://www.apple.com/publicsource and read it before using this file.
11: *
12: * This Original Code and all software distributed under the License are
13: * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14: * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15: * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17: * License for the specific language governing rights and limitations
18: * under the License.
19: *
20: * @APPLE_LICENSE_HEADER_END@
21: */
22:
23: //
24: // Notes:
25: // -----
26: // o the on-disk label structure is packed for M68xxx and has big endian fields
27: //
28: // o the on-disk label is stored four times in succession, each label taking up
29: // round(sizeof(disk_label_t), drive's hard block size) bytes; CD's suffer of
30: // the mastering-with-a-different-hard-block-size problem, however we presume
31: // that all CDs produced that exhibit this problem have a valid first label
32: // at relative byte zero, and hence the next label increment is irrelevant.
33: //
34: // o the dl_label_blkno block value is absolute with respect to the whole disk,
35: // and the implicit block size is the drive's hard block size (eg. hard drive
36: // is typically 512 bytes, CD is typically 2048 bytes)
37: //
38: // o the dl_dt.d_front block value is relative to the containing partition's
39: // boundary (ie. relative to the very first label's dl_label_blkno value),
40: // and the implicit block size is dl_dt.d_secsize bytes (eg. hd=1024, cd=2048)
41: //
42: // o the dl_dt.d_partitions[].p_base block value is absolute with respect to the
43: // whole disk, and the implicit block size is dl_dt.d_secsize bytes; an offset
44: // of dl_dt.d_front is not factored into the p_base value, but should be in
45: // order to calculate the actual start position of the partition on the disk
46: //
47: // o the dl_dt.d_partitions[].p_size block value's implicit block size is
48: // dl_dt.d_secsize bytes
49: //
50: // Policies:
51: // --------
52: // o the NeXT partition scheme accepts probes only on "whole" media objects
53: // and non-whole media objects with a content hint of 'Apple_Rhapsody_UFS'
54: //
55: // o the first two out of the four possible on-disk labels are read while the
56: // last two are ignored -- this is done in the spirit of minimizing reads;
57: // we forfeit some redundancy, however that is acceptable; the second label
58: // MUST be checked due to the fact that i386 machines commonly have the 1st
59: // label missing (due to the FDISK boot structure), but the second is valid
60: //
61: // o the default probe score of the NeXT partition scheme is 1000
62: //
63: // o the eigth partition is always skipped -- this is due to the many varying
64: // preconceptions associated with the 8th partition in unix land: we just
65: // ignore it altogether (+ permits us a significant coding simplification)
66: //
67: // o the checksum for each NeXT label is not validated -- this is done in the
68: // spirit of minimizing reads and perhaps a bit of laziness
69: //
70:
71: #include <IOKit/assert.h>
72: #include <IOKit/IOLib.h>
73: #include <IOKit/storage/IONeXTPartitionScheme.h>
74: #include <libkern/OSByteOrder.h>
75:
76: #define super IOPartitionScheme
77: OSDefineMetaClassAndStructors(IONeXTPartitionScheme, IOPartitionScheme);
78:
79: #define kMinimumBlockSize 512
80:
81: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
82: // NeXT Partition Types
83:
84: static struct { char * type; char * name; } partitionTypes[] =
85: {
86: { "4.4BSD", "Apple_UFS" },
87: // { "4.1BSD", ... }, // V7 with 1K blocks (4.1, 2.9)
88: // { "4.2BSD", ... }, // 4.2BSD fast file system
89: // { "4.4LFS", ... }, // 4.4BSD log-structured file system
90: { 0, 0 }
91: };
92:
93: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
94:
95: bool IONeXTPartitionScheme::init(OSDictionary * properties = 0)
96: {
97: //
98: // Initialize this object's minimal state.
99: //
100:
101: if (super::init(properties) == false) return false;
102:
103: _absoluteBase = 0;
104: _buffer = 0;
105: _bufferSize = 0;
106:
107: // Validate the compiled size of our important fixed-size structures.
108:
109: assert(sizeof(disktab_t) == 514);
110: assert(sizeof(partition_t) == 46);
111: assert(sizeof(disk_label_t) == 7240);
112:
113: return true;
114: }
115:
116: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
117:
118: void IONeXTPartitionScheme::free()
119: {
120: //
121: // Free all of this object's outstanding resources.
122: //
123:
124: if (_buffer) _buffer->release();
125:
126: super::free();
127: }
128:
129: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
130:
131: IOService * IONeXTPartitionScheme::probe(IOService * provider, SInt32 * score)
132: {
133: //
134: // Determine whether the provider media contains an NeXT partition map. If
135: // it does, we return "this" to indicate success, otherwise we return zero.
136: //
137:
138: IOMedia * media = (IOMedia *) provider;
139:
140: // State our assumptions.
141:
142: assert(OSDynamicCast(IOMedia, provider));
143:
144: // Ask superclass' opinion about this probe.
145:
146: if (super::probe(provider, score) == 0) return 0;
147:
148: // Determine whether this media object is unformatted.
149:
150: if (media->isFormatted() == false) return 0;
151:
152: // Determine whether this media's block size is below our assumed minimum.
153:
154: if (media->getPreferredBlockSize() < kMinimumBlockSize) return 0;
155:
156: // Determine whether we can rule out the media object as a NeXT partition
157: // map container without reading actual data from the media. We rule out
158: // all non-whole media objects without an 'Apple_Rhapsody_UFS' hint.
159:
160: if ( media->isWhole() == false )
161: {
162: if ( strcmp(media->getContentHint(), "Apple_Rhapsody_UFS") ) return 0;
163: }
164:
165: // Compute this partition's absolute offset with respect to the whole
166: // media, since the disk_label structure requires this information --
167: // we go down the service hierarchy until we reach the whole media
168: // object (or run off the end :-).
169:
170: for (IOService * service = media; service; service = service->getProvider())
171: {
172: if ( OSDynamicCast(IOMedia, service) ) // (is this a media object?)
173: {
174: _absoluteBase += ((IOMedia *)service)->getBase();
175: if (((IOMedia *)service)->isWhole()) break;
176: }
177: }
178:
179: // Allocate a buffer large enough to hold one media block.
180:
181: _bufferSize = media->getPreferredBlockSize();
182: _buffer = IOBufferMemoryDescriptor::withCapacity(_bufferSize,
183: kIODirectionIn);
184:
185: if (_buffer == 0) return 0;
186:
187: // Search for a valid NeXT label on the provider media.
188:
189: return identify(media) ? this : 0;
190: }
191:
192: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
193:
194: bool IONeXTPartitionScheme::start(IOService * provider)
195: {
196: //
197: // This method is called once we have been attached to the media object. We
198: // generate the new media objects that will represent our partitions here.
199: //
200:
201: disk_label_t * label;
202: IOMedia * media = (IOMedia *) provider;
203: bool success = false;
204:
205: // State our assumptions.
206:
207: assert(_buffer);
208: assert(_bufferSize >= kMinimumBlockSize);
209:
210: // Ask our superclass' opinion.
211:
212: if ( !super::start(provider) ) return false;
213:
214: // The block size we calculate for the partitions is important to
215: // BSD and its filesystems -- if we get it wrong, the filesystem
216: // will end up reading and writing the wrong blocks. We use the
217: // NeXT label's sector size, NOT the partition's fragment size or
218: // it won't work (speaking out of experience). Period.
219:
220: label = (disk_label_t *) _buffer->getBytesNoCopy();
221:
222: UInt64 fsBlockSize = OSSwapBigToHostInt32(label->dl_secsize);
223:
224: if (fsBlockSize % media->getPreferredBlockSize())
225: {
226: IOLog("%s on %s: Bad block size is defined.\n",
227: getName(), media->getName());
228: return false;
229: }
230:
231: // Scan through all the partition entries.
232: //
233: // Due to the different preconceptions associated with the eigth partition,
234: // our policy is to ignore the eigth partition altogether. This also makes
235: // for a cool coding simplification -- since the first 7 partition entries
236: // completely fit in the first 512-byte block, a read of the next block and
237: // caching useful global information from the first block is not necessary
238: // just to parse the eigth partition entry.
239:
240: assert( kMinimumBlockSize >= ( (UInt8 *) &(label->dl_part[NPART-1]) -
241: (UInt8 *) label) );
242:
243: for ( unsigned index = 0; index < NPART - 1; index++ )
244: {
245: // Skip all null partition entries (have base of -1 and size of -1).
246:
247: if ( (SInt32) OSSwapBigToHostInt32(label->dl_part[index].p_base) < 0 ||
248: (SInt32) OSSwapBigToHostInt32(label->dl_part[index].p_size) <= 0 )
249: continue; // (skip)
250:
251: // Compute the absolute position and size of the new partition.
252:
253: UInt64 base;
254: UInt64 size;
255:
256: base = ( (UInt64) OSSwapBigToHostInt32(label->dl_part[index].p_base) +
257: (UInt64) OSSwapBigToHostInt16(label->dl_front) ) *
258: (UInt64) OSSwapBigToHostInt32(label->dl_secsize);
259: size = (UInt64) OSSwapBigToHostInt32(label->dl_part[index].p_size) *
260: (UInt64) OSSwapBigToHostInt32(label->dl_secsize);
261:
262: // Look up a name and a type for this partition.
263:
264: const char * aName = "Untitled";
265: const char * aHint = 0;
266:
267: if ( label->dl_part[index].p_mountpt[0] )
268: aName = label->dl_part[index].p_mountpt;
269: else if ( label->dl_label[0] )
270: aName = label->dl_label;
271:
272: for (unsigned n = 0; partitionTypes[n].type; n++)
273: {
274: if ( !strcmp(label->dl_part[index].p_type, partitionTypes[n].type) )
275: {
276: aHint = partitionTypes[n].name;
277: break;
278: }
279: }
280:
281: if (aHint == 0) aHint = label->dl_part[index].p_type;
282:
283: // Ensure the partition definition does not leave the confines of the
284: // containing media (especially that base is an absolute position).
285:
286: if ( base < _absoluteBase ||
287: base - _absoluteBase + size > media->getSize() )
288: {
289: IOLog("%s on %s: \"%s\" (partition %d) exceeds confines of "
290: "containing media.\n",
291: getName(), media->getName(), aName, index+1);
292: continue; // (skip)
293: }
294:
295: // The partition base may be unaligned with respect the whole media's
296: // block boundaries. We warn the user in this event since every read
297: // or write is going to cause deblocking.
298:
299: if ( (_absoluteBase + base) % media->getPreferredBlockSize() )
300: {
301: IOLog("%s on %s: Access to \"%s\" (partition %d) may be slow due "
302: "to a misaligned block boundary.\n",
303: getName(), media->getName(), aName, index + 1);
304: }
305:
306: // Create the new media object.
307:
308: IOMedia * newMedia = new IOMedia;
309:
310: if ( !newMedia ||
311: !newMedia->init( // base (bytes; relative to provider media)
312: base - _absoluteBase,
313: // size (bytes)
314: size,
315: // natural block size (bytes)
316: fsBlockSize,
317: // is ejectable
318: media->isEjectable(),
319: // is whole
320: false,
321: // is writable
322: media->isWritable(),
323: // content hint
324: aHint ) ||
325: !newMedia->attach(this) )
326: {
327: IOLog("%s on %s: Unable to create media object for \"%s\" "
328: "(partition %d).\n",
329: getName(), media->getName(), aName, index + 1);
330:
331: if (newMedia) newMedia->release();
332: continue; // (skip)
333: }
334:
335: newMedia->setName(aName);
336:
337: // Set a location value (the partition number) for this partition.
338:
339: char location[12];
340: sprintf(location, "%d", index + 1);
341: newMedia->setLocation(location);
342:
343: // Create the "Partition ID" key.
344:
345: newMedia->setProperty(kIOMediaPartitionID, index + 1, 32);
346:
347: // Register the media object with the matching system.
348:
349: newMedia->registerService();
350:
351: // Release our retain on the new media object (in registry).
352:
353: newMedia->release();
354:
355: success = true;
356: } // (for all partition entries)
357:
358: // Release our buffer now that we no longer need it.
359:
360: _buffer->release();
361:
362: _buffer = 0;
363: _bufferSize = 0;
364:
365: // Return success if we found at least one partition.
366:
367: return success;
368: }
369:
370: // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
371:
372: bool IONeXTPartitionScheme::identify(IOMedia * media)
373: {
374: //
375: // Searches for the existence of a NeXT partition map on the given media.
376: //
377:
378: UInt32 labelIncrement; // (in bytes)
379: bool success = false;
380:
381: // State our assumptions.
382:
383: assert(_buffer);
384: assert(_bufferSize >= kMinimumBlockSize);
385:
386: // Open the media object for access.
387:
388: if ( media->open(this, 0, kAccessReader) == false ) return false;
389:
390: // Search through the four (NLABELS) possible label positions.
391: //
392: // In the spirit of minimizing reads, however, we cheat and only check the
393: // first two of the four possible label positions. Note that it is common
394: // on i386 machines that the first label is non-existent, while the second
395: // is valid, hence why we check the first TWO labels.
396:
397: labelIncrement=IORound(sizeof(disk_label_t),media->getPreferredBlockSize());
398:
399: for (unsigned index = 0; index < 2; index++) // ("2" was "NLABELS")
400: {
401: // Read the appropriate block into our buffer.
402:
403: _buffer->setDirection(kIODirectionIn); // (a read)
404: _buffer->setLength(_bufferSize); // (transfer one full block)
405:
406: if ( media->read( /* client */ this,
407: /* byteStart */ labelIncrement * index,
408: /* buffer */ _buffer ) == kIOReturnSuccess )
409: {
410: // Determine whether this buffer contains a valid NeXT label. We
411: // validate the version signature and the label's block position.
412:
413: disk_label_t * label = (disk_label_t *) _buffer->getBytesNoCopy();
414:
415: if ( OSSwapBigToHostInt32(label->dl_version) == DL_V3 ||
416: OSSwapBigToHostInt32(label->dl_version) == DL_V2 ||
417: OSSwapBigToHostInt32(label->dl_version) == DL_V1 )
418: {
419: if ( OSSwapBigToHostInt32(label->dl_label_blkno) ==
420: (UInt32) ( (_absoluteBase + labelIncrement * index) /
421: media->getPreferredBlockSize() ) )
422: {
423: success = true; // (valid version and block position)
424: break;
425: }
426: }
427: }
428: }
429:
430: // Close the media object we opened earlier.
431:
432: media->close(this);
433:
434: return success;
435: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.