1/*
2 * Copyright (c) 1998-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <IOKit/assert.h>
25#include <IOKit/IOBufferMemoryDescriptor.h>
26#include <IOKit/IODeviceTreeSupport.h>
27#include <IOKit/IOLib.h>
28#include <IOKit/storage/IOAppleLabelScheme.h>
29#include <libkern/OSByteOrder.h>
30
31#define super IOFilterScheme
32OSDefineMetaClassAndStructors(IOAppleLabelScheme, IOFilterScheme);
33
34//
35// Notes
36//
37// o the on-disk structure's fields are big-endian formatted
38// o the al_offset value is relative to the media container
39//
40
41#define kIOMediaBaseKey "Base"
42
43bool IOAppleLabelScheme::init(OSDictionary * properties)
44{
45    //
46    // Initialize this object's minimal state.
47    //
48
49    // State our assumptions.
50
51    assert(sizeof(applelabel) == 512);              // (compiler/platform check)
52
53    // Ask our superclass' opinion.
54
55    if (super::init(properties) == false)  return false;
56
57    // Initialize our state.
58
59    _content = 0;
60
61    return true;
62}
63
64void IOAppleLabelScheme::free()
65{
66    //
67    // Free all of this object's outstanding resources.
68    //
69
70    if ( _content )  _content->release();
71
72    super::free();
73}
74
75IOService * IOAppleLabelScheme::probe(IOService * provider, SInt32 * score)
76{
77    //
78    // Determine whether the provider media contains an Apple label scheme.
79    //
80
81    // State our assumptions.
82
83    assert(OSDynamicCast(IOMedia, provider));
84
85    // Ask superclass' opinion.
86
87    if (super::probe(provider, score) == 0)  return 0;
88
89    // Scan the provider media for an Apple label scheme.
90
91    _content = scan(score);
92
93    return ( _content ) ? this : 0;
94}
95
96bool IOAppleLabelScheme::start(IOService * provider)
97{
98    //
99    // Publish the new media object which represents our content.
100    //
101
102    // State our assumptions.
103
104    assert(_content);
105
106    // Ask our superclass' opinion.
107
108    if ( super::start(provider) == false )  return false;
109
110    // Attach and register the new media object representing our content.
111
112    _content->attach(this);
113
114    attachMediaObjectToDeviceTree(_content);
115
116    _content->registerService();
117
118    return true;
119}
120
121void IOAppleLabelScheme::stop(IOService * provider)
122{
123    //
124    // Clean up after the media object we published before terminating.
125    //
126
127    // State our assumptions.
128
129    assert(_content);
130
131    // Detach the media objects we previously attached to the device tree.
132
133    detachMediaObjectFromDeviceTree(_content);
134
135    super::stop(provider);
136}
137
138IOMedia * IOAppleLabelScheme::scan(SInt32 * score)
139{
140    //
141    // Scan the provider media for an Apple label scheme.
142    //
143
144    IOBufferMemoryDescriptor * buffer         = 0;
145    UInt64                     bufferBase     = 0;
146    UInt32                     bufferSize     = 0;
147    applelabel *               headerMap      = 0;
148    UInt64                     labelBase      = 0;
149    UInt32                     labelCheck     = 0;
150    char *                     labelMap       = 0;
151    UInt32                     labelSize      = 0;
152    IOMedia *                  media          = getProvider();
153    UInt64                     mediaBlockSize = media->getPreferredBlockSize();
154    bool                       mediaIsOpen    = false;
155    IOMedia *                  newMedia       = 0;
156    OSDictionary *             properties     = 0;
157    IOReturn                   status         = kIOReturnError;
158
159    // Determine whether this media is formatted.
160
161    if ( media->isFormatted() == false )  goto scanErr;
162
163    // Determine whether this media has an appropriate block size.
164
165    if ( (mediaBlockSize % sizeof(applelabel)) )  goto scanErr;
166
167    // Allocate a buffer large enough to hold one map, rounded to a media block.
168
169    bufferSize = IORound(sizeof(applelabel), mediaBlockSize);
170    buffer     = IOBufferMemoryDescriptor::withCapacity(
171                                           /* capacity      */ bufferSize,
172                                           /* withDirection */ kIODirectionIn );
173    if ( buffer == 0 )  goto scanErr;
174
175    // Open the media with read access.
176
177    mediaIsOpen = media->open(this, 0, kIOStorageAccessReader);
178    if ( mediaIsOpen == false )  goto scanErr;
179
180    // Read the label header into our buffer.
181
182    status = media->read(this, 0, buffer);
183    if ( status != kIOReturnSuccess )  goto scanErr;
184
185    headerMap = (applelabel *) buffer->getBytesNoCopy();
186
187    // Determine whether the label header signature is present.
188
189    if ( OSSwapBigToHostInt16(headerMap->al_magic) != AL_MAGIC )
190    {
191        goto scanErr;
192    }
193
194    // Determine whether the label header version is valid.
195
196    if ( OSSwapBigToHostInt16(headerMap->al_type) != AL_TYPE_DEFAULT )
197    {
198        goto scanErr;
199    }
200
201    // Compute the relative byte position and size of the label.
202
203    labelBase  = OSSwapBigToHostInt64(headerMap->al_offset);
204    labelCheck = OSSwapBigToHostInt32(headerMap->al_checksum);
205    labelSize  = OSSwapBigToHostInt32(headerMap->al_size);
206
207    if ( labelSize > 131072 )
208    {
209        goto scanErr;
210    }
211
212    // Allocate a buffer large enough to hold one map, rounded to a media block.
213
214    buffer->release();
215
216    bufferBase = IOTrunc(labelBase, mediaBlockSize);
217    bufferSize = IORound(labelBase + labelSize, mediaBlockSize) - bufferBase;
218    buffer     = IOBufferMemoryDescriptor::withCapacity(
219                                           /* capacity      */ bufferSize,
220                                           /* withDirection */ kIODirectionIn );
221    if ( buffer == 0 )  goto scanErr;
222
223    // Determine whether the label leaves the confines of the container.
224
225    if ( bufferBase + bufferSize > media->getSize() )  goto scanErr;
226
227    // Read the label into our buffer.
228
229    status = media->read(this, bufferBase, buffer);
230    if ( status != kIOReturnSuccess )  goto scanErr;
231
232    labelMap = (char *) buffer->getBytesNoCopy() + (labelBase % mediaBlockSize);
233
234    // Determine whether the label checksum is valid.
235
236    if ( crc32(0, labelMap, labelSize) != labelCheck )
237    {
238        goto scanErr;
239    }
240
241    // Obtain the properties.
242
243    properties = (OSDictionary *) OSUnserializeXML(labelMap, labelSize);
244
245    if ( OSDynamicCast(OSDictionary, properties) == 0 )
246    {
247        goto scanErr;
248    }
249
250    // Determine whether the content is corrupt.
251
252    if ( isContentCorrupt(properties) )
253    {
254        goto scanErr;
255    }
256
257    // Determine whether the content is corrupt.
258
259    if ( isContentInvalid(properties) )
260    {
261        goto scanErr;
262    }
263
264    // Create a media object to represent the content.
265
266    newMedia = instantiateMediaObject(properties);
267
268    if ( newMedia == 0 )
269    {
270        goto scanErr;
271    }
272
273    // Release our resources.
274
275    media->close(this);
276    buffer->release();
277    properties->release();
278
279    return newMedia;
280
281scanErr:
282
283    // Release our resources.
284
285    if ( mediaIsOpen )  media->close(this);
286    if ( buffer )  buffer->release();
287    if ( properties )  properties->release();
288
289    return 0;
290}
291
292bool IOAppleLabelScheme::isContentCorrupt(OSDictionary * properties)
293{
294    //
295    // Ask whether the given content appears to be corrupt.
296    //
297
298    return false;
299}
300
301bool IOAppleLabelScheme::isContentInvalid(OSDictionary * properties)
302{
303    //
304    // Ask whether the given content appears to be invalid.
305    //
306
307    UInt64     contentBase = 0;
308    UInt64     contentSize = 0;
309    IOMedia *  media       = getProvider();
310    OSObject * object      = 0;
311
312    // Compute the relative byte position and size of the new content.
313
314    object = properties->getObject(kIOMediaBaseKey);
315
316    if ( OSDynamicCast(OSNumber, object) )
317    {
318        contentBase = ((OSNumber *) object)->unsigned64BitValue();
319    }
320
321    object = properties->getObject(kIOMediaSizeKey);
322
323    if ( OSDynamicCast(OSNumber, object) )
324    {
325        contentSize = ((OSNumber *) object)->unsigned64BitValue();
326    }
327
328    // Determine whether the content is a placeholder.
329
330    if ( contentSize == 0 )  return true;
331
332    // Determine whether the new content leaves the confines of the container.
333
334    if ( contentBase + contentSize > media->getSize() )  return true;
335
336    return false;
337}
338
339IOMedia * IOAppleLabelScheme::instantiateMediaObject(OSDictionary * properties)
340{
341    //
342    // Instantiate a new media object to represent the given content.
343    //
344
345    IOMediaAttributeMask contentAttributes = 0;
346    UInt64               contentBase       = 0;
347    UInt64               contentBlockSize  = 0;
348    const char *         contentHint       = 0;
349    bool                 contentIsWhole    = false;
350    bool                 contentIsWritable = false;
351    UInt64               contentSize       = 0;
352    IOMedia *            media             = getProvider();
353    IOMedia *            newMedia          = 0;
354    OSObject *           object            = 0;
355
356    contentAttributes = media->getAttributes();
357    contentBlockSize  = media->getPreferredBlockSize();
358    contentIsWhole    = media->isWhole();
359    contentIsWritable = media->isWritable();
360    contentSize       = media->getSize();
361
362    properties = OSDictionary::withDictionary(properties);
363    if ( properties == 0 )  return 0;
364
365    // Obtain the initialization properties.
366
367    object = properties->getObject(kIOMediaBaseKey);
368
369    if ( OSDynamicCast(OSNumber, object) )
370    {
371        contentBase = ((OSNumber *) object)->unsigned64BitValue();
372    }
373
374    properties->removeObject(kIOMediaBaseKey);
375
376    object = properties->getObject(kIOMediaContentKey);
377
378    if ( OSDynamicCast(OSString, object) )
379    {
380        contentHint = ((OSString *) object)->getCStringNoCopy();
381    }
382
383    properties->removeObject(kIOMediaContentKey);
384
385    object = properties->getObject(kIOMediaContentHintKey);
386
387    if ( OSDynamicCast(OSString, object) )
388    {
389        contentHint = ((OSString *) object)->getCStringNoCopy();
390    }
391
392    properties->removeObject(kIOMediaContentHintKey);
393
394    object = properties->getObject(kIOMediaEjectableKey);
395
396    if ( OSDynamicCast(OSBoolean, object) )
397    {
398        if ( ((OSBoolean *) object)->getValue() )
399        {
400            contentAttributes |= kIOMediaAttributeEjectableMask;
401        }
402        else
403        {
404            contentAttributes &= ~kIOMediaAttributeEjectableMask;
405        }
406    }
407
408    properties->removeObject(kIOMediaEjectableKey);
409
410    object = properties->getObject(kIOMediaPreferredBlockSizeKey);
411
412    if ( OSDynamicCast(OSNumber, object) )
413    {
414        contentBlockSize = ((OSNumber *) object)->unsigned64BitValue();
415    }
416
417    properties->removeObject(kIOMediaPreferredBlockSizeKey);
418
419    object = properties->getObject(kIOMediaRemovableKey);
420
421    if ( OSDynamicCast(OSBoolean, object) )
422    {
423        if ( ((OSBoolean *) object)->getValue() )
424        {
425            contentAttributes |= kIOMediaAttributeRemovableMask;
426        }
427        else
428        {
429            contentAttributes &= ~kIOMediaAttributeRemovableMask;
430        }
431    }
432
433    properties->removeObject(kIOMediaRemovableKey);
434
435    object = properties->getObject(kIOMediaWholeKey);
436
437    if ( OSDynamicCast(OSBoolean, object) )
438    {
439        contentIsWhole = ((OSBoolean *) object)->getValue();
440    }
441
442    properties->removeObject(kIOMediaWholeKey);
443
444    object = properties->getObject(kIOMediaSizeKey);
445
446    if ( OSDynamicCast(OSNumber, object) )
447    {
448        contentSize = ((OSNumber *) object)->unsigned64BitValue();
449    }
450
451    properties->removeObject(kIOMediaSizeKey);
452
453    object = properties->getObject(kIOMediaWritableKey);
454
455    if ( OSDynamicCast(OSBoolean, object) )
456    {
457        contentIsWritable = ((OSBoolean *) object)->getValue();
458    }
459
460    properties->removeObject(kIOMediaWritableKey);
461
462    // Create the new media object.
463
464    newMedia = instantiateDesiredMediaObject(properties);
465
466    if ( newMedia )
467    {
468        if ( newMedia->init(
469                /* base               */ contentBase,
470                /* size               */ contentSize,
471                /* preferredBlockSize */ contentBlockSize,
472                /* attributes         */ contentAttributes,
473                /* isWhole            */ contentIsWhole,
474                /* isWritable         */ contentIsWritable,
475                /* contentHint        */ contentHint ) )
476        {
477            // Set a location.
478
479            newMedia->setLocation("1");
480
481            // Set the properties.
482
483            newMedia->getPropertyTable()->merge(properties);
484        }
485        else
486        {
487            newMedia->release();
488            newMedia = 0;
489        }
490    }
491
492    properties->release();
493
494    return newMedia;
495}
496
497IOMedia * IOAppleLabelScheme::instantiateDesiredMediaObject(
498                                                     OSDictionary * properties )
499{
500    //
501    // Allocate a new media object (called from instantiateMediaObject).
502    //
503
504    return new IOMedia;
505}
506
507bool IOAppleLabelScheme::attachMediaObjectToDeviceTree( IOMedia * media )
508{
509    //
510    // Attach the given media object to the device tree plane.
511    //
512
513    IORegistryEntry * child;
514
515    if ( (child = getParentEntry(gIOServicePlane)) )
516    {
517        IORegistryEntry * parent;
518
519        if ( (parent = child->getParentEntry(gIODTPlane)) )
520        {
521            const char * location = child->getLocation(gIODTPlane);
522            const char * name     = child->getName(gIODTPlane);
523
524            if ( media->attachToParent(parent, gIODTPlane) )
525            {
526                media->setLocation(location, gIODTPlane);
527                media->setName(name, gIODTPlane);
528
529                child->detachFromParent(parent, gIODTPlane);
530
531                return true;
532            }
533        }
534    }
535
536    return false;
537}
538
539void IOAppleLabelScheme::detachMediaObjectFromDeviceTree( IOMedia * media )
540{
541    //
542    // Detach the given media object from the device tree plane.
543    //
544
545    IORegistryEntry * child;
546
547    if ( (child = getParentEntry(gIOServicePlane)) )
548    {
549        IORegistryEntry * parent;
550
551        if ( (parent = media->getParentEntry(gIODTPlane)) )
552        {
553            const char * location = media->getLocation(gIODTPlane);
554            const char * name     = media->getName(gIODTPlane);
555
556            if ( child->attachToParent(parent, gIODTPlane) )
557            {
558                child->setLocation(location, gIODTPlane);
559                child->setName(name, gIODTPlane);
560            }
561
562            media->detachFromParent(parent, gIODTPlane);
563        }
564    }
565}
566
567OSMetaClassDefineReservedUnused(IOAppleLabelScheme,  0);
568OSMetaClassDefineReservedUnused(IOAppleLabelScheme,  1);
569OSMetaClassDefineReservedUnused(IOAppleLabelScheme,  2);
570OSMetaClassDefineReservedUnused(IOAppleLabelScheme,  3);
571OSMetaClassDefineReservedUnused(IOAppleLabelScheme,  4);
572OSMetaClassDefineReservedUnused(IOAppleLabelScheme,  5);
573OSMetaClassDefineReservedUnused(IOAppleLabelScheme,  6);
574OSMetaClassDefineReservedUnused(IOAppleLabelScheme,  7);
575OSMetaClassDefineReservedUnused(IOAppleLabelScheme,  8);
576OSMetaClassDefineReservedUnused(IOAppleLabelScheme,  9);
577OSMetaClassDefineReservedUnused(IOAppleLabelScheme, 10);
578OSMetaClassDefineReservedUnused(IOAppleLabelScheme, 11);
579OSMetaClassDefineReservedUnused(IOAppleLabelScheme, 12);
580OSMetaClassDefineReservedUnused(IOAppleLabelScheme, 13);
581OSMetaClassDefineReservedUnused(IOAppleLabelScheme, 14);
582OSMetaClassDefineReservedUnused(IOAppleLabelScheme, 15);
583