1 /*
2 * Copyright 2012, Jérôme Duval, korli@users.berlios.de.
3 * Copyright 2008, Salvatore Benedetto, salvatore.benedetto@gmail.com
4 * Copyright 2003, Tyler Dauwalder, tyler@dauwalder.net.
5 * Distributed under the terms of the MIT License.
6 */
7
8 #include "Volume.h"
9
10 #include "Icb.h"
11 #include "MemoryChunk.h"
12 #include "MetadataPartition.h"
13 #include "PhysicalPartition.h"
14 #include "Recognition.h"
15
16 extern fs_volume_ops gUDFVolumeOps;
17 extern fs_vnode_ops gUDFVnodeOps;
18
19 /*! \brief Creates an unmounted volume with the given id. */
Volume(fs_volume * fsVolume)20 Volume::Volume(fs_volume *fsVolume)
21 :
22 fBlockCache(NULL),
23 fBlockShift(0),
24 fBlockSize(0),
25 fDevice(-1),
26 fFSVolume(fsVolume),
27 fLength(0),
28 fMounted(false),
29 fOffset(0),
30 fRootIcb(NULL)
31 {
32 for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++)
33 fPartitions[i] = NULL;
34 }
35
36
~Volume()37 Volume::~Volume()
38 {
39 _Unset();
40 }
41
42
43 /*! \brief Attempts to mount the given device.
44
45 \param lenght The length of the device in number of blocks
46 */
47 status_t
Mount(const char * deviceName,off_t offset,off_t length,uint32 blockSize,uint32 flags)48 Volume::Mount(const char *deviceName, off_t offset, off_t length,
49 uint32 blockSize, uint32 flags)
50 {
51 TRACE(("Volume::Mount: deviceName = `%s', offset = %" B_PRIdOFF ", length "
52 "= %" B_PRIdOFF ", blockSize: %" B_PRIu32 ", flags: %" B_PRIu32 "\n",
53 deviceName, offset, length, blockSize, flags));
54 if (!deviceName)
55 return B_BAD_VALUE;
56 if (Mounted())
57 // Already mounted, thank you for asking
58 return B_BUSY;
59
60 // Open the device read only
61 int device = open(deviceName, O_RDONLY);
62 if (device < B_OK) {
63 TRACE_ERROR(("Volume::Mount: failed to open device = %s\n", deviceName));
64 return device;
65 }
66
67 DEBUG_INIT_ETC("Volume", ("deviceName: %s", deviceName));
68 status_t status = B_OK;
69
70 // If the device is actually a normal file, try to disable the cache
71 // for the file in the parent filesystem
72 #if 0 // _KERNEL_MODE
73 struct stat stat;
74 error = fstat(device, &stat) < 0 ? B_ERROR : B_OK;
75 if (!error) {
76 if (stat.st_mode & S_IFREG && ioctl(device, IOCTL_FILE_UNCACHED_IO, NULL) < 0) {
77 DIE(("Unable to disable cache of underlying file system.\n"));
78 }
79 }
80 #endif
81
82 logical_volume_descriptor logicalVolumeDescriptor;
83 partition_descriptor partitionDescriptors[kMaxPartitionDescriptors];
84 uint8 partitionDescriptorCount;
85 uint32 blockShift;
86
87 // Run through the volume recognition and descriptor sequences to
88 // see if we have a potentially valid UDF volume on our hands
89 status = udf_recognize(device, offset, length, blockSize, blockShift,
90 fPrimaryVolumeDescriptor, logicalVolumeDescriptor,
91 partitionDescriptors, partitionDescriptorCount);
92
93 // Set up the block cache
94 if (!status) {
95 TRACE(("Volume::Mount: partition recognized\n"));
96 fBlockCache = block_cache_create(device, length, blockSize, IsReadOnly());
97 } else {
98 TRACE_ERROR(("Volume::Mount: failed to recognize partition\n"));
99 return status;
100 }
101
102 int physicalCount = 0;
103 int virtualCount = 0;
104 int sparableCount = 0;
105 int metadataCount = 0;
106
107 // Set up the partitions
108 // Set up physical and sparable partitions first
109 offset = 0;
110 for (uint8 i = 0; i < logicalVolumeDescriptor.partition_map_count()
111 && !status; i++)
112 {
113 uint8 *maps = logicalVolumeDescriptor.partition_maps();
114 partition_map_header *header = (partition_map_header *)(maps + offset);
115 TRACE(("Volume::Mount: partition map %d (type %d):\n", i,
116 header->type()));
117 if (header->type() == 1) {
118 TRACE(("Volume::Mount: map type -> physical\n"));
119 physical_partition_map* map = (physical_partition_map *)header;
120 // Find the corresponding partition descriptor
121 partition_descriptor *descriptor = NULL;
122 for (uint8 j = 0; j < partitionDescriptorCount; j++) {
123 if (map->partition_number() ==
124 partitionDescriptors[j].partition_number()) {
125 descriptor = &partitionDescriptors[j];
126 break;
127 }
128 }
129 // Create and add the partition
130 if (descriptor) {
131 PhysicalPartition *partition
132 = new(nothrow) PhysicalPartition(map->partition_number(),
133 descriptor->start(), descriptor->length());
134 status = partition ? B_OK : B_NO_MEMORY;
135 if (!status) {
136 TRACE(("Volume::Mount: adding PhysicalPartition(number: %d, "
137 "start: %" B_PRIu32 ", length: %" B_PRIu32 ")\n",
138 map->partition_number(), descriptor->start(),
139 descriptor->length()));
140 status = _SetPartition(i, partition);
141 if (!status)
142 physicalCount++;
143 }
144 } else {
145 TRACE_ERROR(("Volume::Mount: no matching partition descriptor found!\n"));
146 status = B_ERROR;
147 }
148 } else if (header->type() == 2) {
149 // Figure out what kind of type 2 partition map we have based
150 // on the type identifier
151 const entity_id &typeId = header->partition_type_id();
152 DUMP(typeId);
153 DUMP(kSparablePartitionMapId);
154 if (typeId.matches(kVirtualPartitionMapId)) {
155 TRACE(("map type: virtual\n"));
156 virtual_partition_map* map =
157 reinterpret_cast<virtual_partition_map*>(header);
158 virtualCount++;
159 (void)map; // kill the warning for now
160 } else if (typeId.matches(kSparablePartitionMapId)) {
161 TRACE(("map type: sparable\n"));
162 sparable_partition_map* map =
163 reinterpret_cast<sparable_partition_map*>(header);
164 sparableCount++;
165 (void)map; // kill the warning for now
166 } else if (typeId.matches(kMetadataPartitionMapId)) {
167 TRACE(("map type: metadata\n"));
168 metadata_partition_map* map =
169 reinterpret_cast<metadata_partition_map*>(header);
170
171
172 // Find the corresponding partition descriptor
173 partition_descriptor *descriptor = NULL;
174 for (uint8 j = 0; j < partitionDescriptorCount; j++) {
175 if (map->partition_number() ==
176 partitionDescriptors[j].partition_number()) {
177 descriptor = &partitionDescriptors[j];
178 break;
179 }
180 }
181 Partition *parent = _GetPartition(map->partition_number());
182 // Create and add the partition
183 if (descriptor != NULL && parent != NULL) {
184 MetadataPartition *partition
185 = new(nothrow) MetadataPartition(this,
186 map->partition_number(), *parent,
187 map->metadata_file_location(),
188 map->metadata_mirror_file_location(),
189 map->metadata_bitmap_file_location(),
190 map->allocation_unit_size(),
191 map->alignment_unit_size(),
192 map->flags() & 1);
193 status = partition ? partition->InitCheck() : B_NO_MEMORY;
194 if (!status) {
195 TRACE(("Volume::Mount: adding MetadataPartition()"));
196 status = _SetPartition(i, partition);
197 if (status == B_OK)
198 metadataCount++;
199 } else {
200 TRACE_ERROR(("Volume::Mount: metadata partition "
201 "creation failed! 0x%" B_PRIx32 "\n", status));
202 }
203 } else {
204 TRACE_ERROR(("Volume::Mount: no matching partition descriptor found!\n"));
205 status = B_ERROR;
206 }
207 } else {
208 TRACE(("map type: unrecognized (`%.23s')\n",
209 typeId.identifier()));
210 status = B_ERROR;
211 }
212 } else {
213 TRACE(("Invalid partition type %d found!\n", header->type()));
214 status = B_ERROR;
215 }
216 offset += header->length();
217 }
218
219 // Do some checking as to what sorts of partitions we've actually found.
220 if (!status) {
221 status = (physicalCount == 1 && virtualCount == 0
222 && sparableCount == 0)
223 || (physicalCount == 2 && virtualCount == 0
224 && sparableCount == 0)
225 ? B_OK : B_ERROR;
226 if (status) {
227 TRACE(("Invalid partition layout found:\n"));
228 TRACE((" physical partitions: %d\n", physicalCount));
229 TRACE((" virtual partitions: %d\n", virtualCount));
230 TRACE((" sparable partitions: %d\n", sparableCount));
231 TRACE((" metadata partitions: %d\n", metadataCount));
232 }
233 }
234
235 // We're now going to start creating Icb's, which will expect
236 // certain parts of the volume to be initialized properly. Thus,
237 // we initialize those parts here.
238 if (!status) {
239 fDevice = device;
240 fOffset = offset;
241 fLength = length;
242 fBlockSize = blockSize;
243 fBlockShift = blockShift;
244 }
245 TRACE(("Volume::Mount: device = %d, offset = %" B_PRIdOFF ", length = %"
246 B_PRIdOFF ", blockSize = %" B_PRIu32 ", blockShift = %" B_PRIu32 "\n",
247 device, offset, length, blockSize, blockShift));
248 // At this point we've found a valid set of volume descriptors and
249 // our partitions are all set up. We now need to investigate the file
250 // set descriptor pointed to by the logical volume descriptor.
251 if (!status) {
252 TRACE(("Volume::Mount: Partition has been set up\n"));
253 MemoryChunk chunk(logicalVolumeDescriptor.file_set_address().length());
254
255 status = chunk.InitCheck();
256
257 if (!status) {
258 off_t address;
259 // Read in the file set descriptor
260 status = MapBlock(logicalVolumeDescriptor.file_set_address(),
261 &address);
262 if (!status)
263 address <<= blockShift;
264 if (!status) {
265 ssize_t bytesRead
266 = read_pos(device, address, chunk.Data(), blockSize);
267 if (bytesRead != ssize_t(blockSize)) {
268 status = B_IO_ERROR;
269 TRACE_ERROR(("read_pos(pos:%" B_PRIdOFF ", len:%" B_PRIu32
270 ") failed with: 0x%lx\n", address, blockSize,
271 bytesRead));
272 }
273 }
274 // See if it's valid, and if so, create the root icb
275 if (!status) {
276 file_set_descriptor *fileSet =
277 reinterpret_cast<file_set_descriptor*>(chunk.Data());
278 PDUMP(fileSet);
279 status = fileSet->tag().id() == TAGID_FILE_SET_DESCRIPTOR
280 ? B_OK : B_ERROR;
281 if (!status)
282 status = fileSet->tag().init_check(
283 logicalVolumeDescriptor.file_set_address().block());
284 if (!status) {
285 PDUMP(fileSet);
286 fRootIcb = new(nothrow) Icb(this, fileSet->root_directory_icb());
287 if (fRootIcb == NULL || fRootIcb->InitCheck() != B_OK)
288 return B_NO_MEMORY;
289 }
290
291 TRACE(("Volume::Mount: Root Node id = %" B_PRIdINO "\n",
292 fRootIcb->Id()));
293 if (!status) {
294 status = publish_vnode(fFSVolume, fRootIcb->Id(), fRootIcb,
295 &gUDFVnodeOps, fRootIcb->Mode(), 0);
296 if (status != B_OK) {
297 TRACE_ERROR(("Error creating vnode for root icb! "
298 "status = 0x%" B_PRIx32 ", `%s'\n", status,
299 strerror(status)));
300 // Clean up the icb we created, since _Unset()
301 // won't do this for us.
302 delete fRootIcb;
303 fRootIcb = NULL;
304 }
305 TRACE(("Volume::Mount: Root vnode published. Id = %"
306 B_PRIdINO "\n", fRootIcb->Id()));
307 }
308 }
309 }
310 }
311
312 // If we've made it this far, we're good to go; set the volume
313 // name and then flag that we're mounted. On the other hand, if
314 // an error occurred, we need to clean things up.
315 if (!status) {
316 fName.SetTo(logicalVolumeDescriptor.logical_volume_identifier());
317 fMounted = true;
318 } else {
319 _Unset();
320 }
321
322 RETURN(status);
323 }
324
325
326 const char*
Name() const327 Volume::Name() const {
328 return fName.Utf8();
329 }
330
331 /*! \brief Maps the given logical block to a physical block.
332 */
333 status_t
MapBlock(long_address address,off_t * mappedBlock)334 Volume::MapBlock(long_address address, off_t *mappedBlock)
335 {
336 TRACE(("Volume::MapBlock: partition = %d, block = %" B_PRIu32
337 ", mappedBlock = %p\n", address.partition(), address.block(),
338 mappedBlock));
339 DEBUG_INIT_ETC("Volume", ("partition = %d, block = %" B_PRIu32
340 ", mappedBlock = %p", address.partition(), address.block(),
341 mappedBlock));
342 status_t error = mappedBlock ? B_OK : B_BAD_VALUE;
343 if (!error) {
344 Partition *partition = _GetPartition(address.partition());
345 error = partition ? B_OK : B_BAD_ADDRESS;
346 if (!error)
347 error = partition->MapBlock(address.block(), *mappedBlock);
348 }
349 RETURN(error);
350 }
351
352 /*! \brief Unsets the volume and deletes any partitions.
353
354 Does *not* delete the root icb object.
355 */
356 void
_Unset()357 Volume::_Unset()
358 {
359 DEBUG_INIT("Volume");
360 // delete our partitions
361 for (int i = 0; i < UDF_MAX_PARTITION_MAPS; i++)
362 _SetPartition(i, NULL);
363 fFSVolume->id = 0;
364 if (fDevice >= 0) {
365 block_cache_delete(fBlockCache, true);
366 close(fDevice);
367 }
368 fBlockCache = NULL;
369 fDevice = -1;
370 fMounted = false;
371 fOffset = 0;
372 fLength = 0;
373 fBlockSize = 0;
374 fBlockShift = 0;
375 fName.SetTo("", 0);
376 }
377
378 /*! \brief Sets the partition associated with the given number after
379 deleting any previously associated partition.
380
381 \param number The partition number (should be the same as the index
382 into the lvd's partition map array).
383 \param partition The new partition (may be NULL).
384 */
385 status_t
_SetPartition(uint number,Partition * partition)386 Volume::_SetPartition(uint number, Partition *partition)
387 {
388 status_t error = number < UDF_MAX_PARTITION_MAPS
389 ? B_OK : B_BAD_VALUE;
390 if (!error) {
391 delete fPartitions[number];
392 fPartitions[number] = partition;
393 }
394 return error;
395 }
396
397 /*! \brief Returns the partition associated with the given number, or
398 NULL if no such partition exists or the number is invalid.
399 */
400 Partition*
_GetPartition(uint number)401 Volume::_GetPartition(uint number)
402 {
403 return (number < UDF_MAX_PARTITION_MAPS)
404 ? fPartitions[number] : NULL;
405 }
406