exfat: fix problems with hrev46820

* Set the size of the volume name member var to B_FILE_NAME_LENGTH which is the
  max width of a volume name according to the BeBook and the Support Kit code. We'll
  deal with trying to stuff the volume name back into the exfat volume label later.
* Rename volume_name() to get_volume_name()
* Pass the name string length into get_volume_name() and use it to avoid buffer overrun.
* Fill name with a blank string if volume has no label.
* Don't memset the name with zeros before doing the conversion from Unicode to UTF-8,
  the conversion function will make sure the result is NUL-terminated if it returns B_OK.
* Also check the return value of the Unicode conversion function and return an error if it fails.
* Add get_default_volume_name() method to Utility.cpp which is used to fill out the default
  volume name in the case volume name is blank. e.g. 32GiB ExFAT Volume. This now
  applies to both the volume name and mount point.
* Use non-metric prefixes for default volume name, e.g. MiB, GiB, TiB
* For an unset volume name fill the volume name with an empty string.
* Remove the leading underscore from _name and _partition parameters
* Replace size constants with sizeof() calls
* Remove Axel from the copyright statement in Utility.cpp, he had nothing to do with it,
  add Jérôme (aka korli) because he wrote the code for get_default_volume_name()
  (was in Volumes.cpp)
* Remove some trailing spaces from encodings.cpp

Thanks Axel.
This commit is contained in:
John Scipione 2014-02-05 20:07:54 -05:00
parent 88e24cf64f
commit dae266a8a4
6 changed files with 84 additions and 71 deletions

View File

@ -1,11 +1,11 @@
/*
* Copyright 2001-2009, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2011, Jérôme Duval, korli@users.berlios.de.
* Copyright 2014 Haiku, Inc. All rights reserved.
*
* Distributed under the terms of the MIT License.
*
* Authors:
* Axel Dörfler, axeld@pinc-software.de
* Jérôme Duval, korli@users.berlios.de
* John Scipione, jscipione@gmail.com
*/
@ -22,29 +22,47 @@
status_t
volume_name(struct exfat_entry* entry, char* _name)
get_volume_name(struct exfat_entry* entry, char* name, size_t length)
{
if (entry == NULL || _name == NULL)
if (entry == NULL || name == NULL)
return B_BAD_VALUE;
if (entry->type == EXFAT_ENTRY_TYPE_NOT_IN_USE) {
const char* untitled = "Untitled";
size_t length = strlen(untitled);
strncpy(_name, untitled, length);
if (strlen(_name) < length)
return B_NAME_TOO_LONG;
} else if (entry->type == EXFAT_ENTRY_TYPE_LABEL) {
if (entry->type == EXFAT_ENTRY_TYPE_NOT_IN_USE)
name = "";
else if (entry->type == EXFAT_ENTRY_TYPE_LABEL) {
// UCS-2 can encode codepoints in the range U+0000 to U+FFFF
// UTF-8 needs at most 3 bytes to encode values in this range
size_t utf8NameLength = entry->label.length * 3;
memset(_name, 0, utf8NameLength + 1);
// zero out the character array
unicode_to_utf8((const uchar*)entry->label.name,
entry->label.length * 2, (uint8*)_name, &utf8NameLength);
if (strlen(_name) < utf8NameLength)
if (length < utf8NameLength)
return B_NAME_TOO_LONG;
status_t result = unicode_to_utf8((const uchar*)entry->label.name,
entry->label.length * 2, (uint8*)name, &utf8NameLength);
if (result != B_OK)
return result;
} else
return B_NAME_NOT_FOUND;
return B_OK;
}
void
get_default_volume_name(off_t diskSize, char* name, size_t length)
{
off_t divisor = 1ULL << 40;
char unit = 'T';
if (diskSize < divisor) {
divisor = 1UL << 30;
unit = 'G';
if (diskSize < divisor) {
divisor = 1UL << 20;
unit = 'M';
}
}
double size = double((10 * diskSize + divisor - 1) / divisor);
// %g in the kernel does not support precision...
snprintf(name, length, "%g%ciB ExFAT Volume", size / 10, unit);
}

View File

@ -45,16 +45,31 @@ open_mode_to_access(int openMode)
}
/*! Reads the volume name from an exfat entry and writes it to \a _name
as a UTF-8 char array. Writes "Untitled" The volume name is not set.
\returns A status code.
\retval B_OK Wrote the volume name successfully.
\retval B_BAD_VALUE \a entry or \a _name was \c NULL.
\retval B_NAME_NOT_FOUND Volume name was not found in this entry.
\retval B_NAME_TOO_LONG The passed in _name wasn't long enough to
fit the name.
/*! Reads the volume name from an exfat entry and writes it to
\a _name as a UTF-8 char array.
Writes a blank string to \a name if the volume name is not set.
\param entry The \a entry to look for the volume name in.
\param name The \a name array to fill out.
\param length The \a length of the name array in bytes.
\returns A status code, \c B_OK on success or an error code otherwise.
\retval B_OK Wrote the volume name to \a name successfully.
\retval B_BAD_VALUE \a entry or \a name was \c NULL.
\retval B_NAME_NOT_FOUND Volume name was not found in this \a entry.
\retval B_NAME_TOO_LONG \a name wasn't long enough to fit the volume name.
*/
status_t volume_name(struct exfat_entry* entry, char* _name);
status_t get_volume_name(struct exfat_entry* entry, char* name, size_t length);
/*! Writes a more or less descriptive volume name to \a name.
\param diskSize The disk size in bytes
\param name The \a name array to fill out.
\param length The \a length of the name array in bytes.
*/
void get_default_volume_name(off_t diskSize, char* name, size_t length);
#endif // UTILITY_H

View File

@ -230,8 +230,8 @@ bool
LabelVisitor::VisitLabel(struct exfat_entry* entry)
{
TRACE("LabelVisitor::VisitLabel()\n");
char name[34];
status_t result = volume_name(entry, name);
char name[B_FILE_NAME_LENGTH];
status_t result = get_volume_name(entry, name, sizeof(name));
if (result != B_OK)
return false;
@ -334,13 +334,6 @@ Volume::Mount(const char* deviceName, uint32 flags)
fEntriesPerBlock = (fBlockSize / sizeof(struct exfat_entry));
// check if the device size is large enough to hold the file system
off_t diskSize;
status = opener.GetSize(&diskSize);
if (status != B_OK)
return status;
if (diskSize < (off_t)fSuperBlock.NumBlocks() << fSuperBlock.BlockShift())
return B_BAD_VALUE;
fBlockCache = opener.InitCache(fSuperBlock.NumBlocks(), fBlockSize);
if (fBlockCache == NULL)
return B_ERROR;
@ -371,23 +364,9 @@ Volume::Mount(const char* deviceName, uint32 flags)
iterator.Iterate(visitor);
if (fName[0] == '\0') {
// generate a more or less descriptive volume name
off_t divisor = 1ULL << 40;
char unit = 'T';
if (diskSize < divisor) {
divisor = 1UL << 30;
unit = 'G';
if (diskSize < divisor) {
divisor = 1UL << 20;
unit = 'M';
}
}
double size = double((10 * diskSize + divisor - 1) / divisor);
// %g in the kernel does not support precision...
snprintf(fName, sizeof(fName), "%g %cB ExFAT Volume",
size / 10, unit);
off_t deviceSize = (off_t)fSuperBlock.NumBlocks()
<< fSuperBlock.BlockShift();
get_default_volume_name(deviceSize, fName, sizeof(fName));
}
return B_OK;

View File

@ -17,6 +17,8 @@
#include <lock.h>
#include <string.h>
#include <StorageDefs.h>
#include "exfat.h"
#include "SplayTree.h"
@ -151,9 +153,7 @@ private:
fs_volume* fFSVolume;
int fDevice;
exfat_super_block fSuperBlock;
char fName[34];
// Max number of bytes needed is (11 * 3) + 1 for the \0,
// that is 11 UCS-2 characters converted to UTF-8.
char fName[B_FILE_NAME_LENGTH];
uint16 fFlags;
uint32 fBlockSize;

View File

@ -134,7 +134,7 @@ _lendian_unicode_to_utf8(
*srcLen = srcCount;
*dstLen = dstCount;
dst[dstCount] = '\0';
return status;
}
@ -174,7 +174,7 @@ _utf8_to_lendian_unicode(
dst[dstCount++] = unicode >> 8;
srcCount += UTF8 - ((uchar *)(src + srcCount));
status = B_OK;
status = B_OK;
}
*srcLen = srcCount;
@ -217,4 +217,3 @@ status_t utf8_to_unicode(const char *utf8, uchar *uni, size_t *unilen)
return result;
}

View File

@ -24,6 +24,7 @@
#include <fs_info.h>
#include <io_requests.h>
#include <NodeMonitor.h>
#include <StorageDefs.h>
#include <util/AutoLock.h>
#include "DirectoryIterator.h"
@ -46,7 +47,7 @@
struct identify_cookie {
exfat_super_block super_block;
char name[34];
char name[B_FILE_NAME_LENGTH];
};
@ -77,7 +78,7 @@ iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
static float
exfat_identify_partition(int fd, partition_data* _partition, void** _cookie)
exfat_identify_partition(int fd, partition_data* partition, void** _cookie)
{
struct exfat_super_block superBlock;
status_t status = Volume::Identify(fd, &superBlock);
@ -89,7 +90,7 @@ exfat_identify_partition(int fd, partition_data* _partition, void** _cookie)
return -1;
memcpy(&cookie->super_block, &superBlock, sizeof(exfat_super_block));
memset(cookie->name, 0, 34);
memset(cookie->name, 0, sizeof(cookie->name));
// zero out volume name
uint32 rootDirCluster = superBlock.RootDirCluster();
@ -104,7 +105,8 @@ exfat_identify_partition(int fd, partition_data* _partition, void** _cookie)
&entry, entrySize) == (ssize_t)entrySize; i++) {
if (entry.type == EXFAT_ENTRY_TYPE_NOT_IN_USE
|| entry.type == EXFAT_ENTRY_TYPE_LABEL) {
if (volume_name(&entry, cookie->name) != B_OK) {
if (get_volume_name(&entry, cookie->name, sizeof(cookie->name))
!= B_OK) {
delete cookie;
return -1;
}
@ -113,8 +115,8 @@ exfat_identify_partition(int fd, partition_data* _partition, void** _cookie)
}
if (cookie->name[0] == '\0') {
delete cookie;
return -1;
off_t deviceSize = (off_t)superBlock.NumBlocks() << superBlock.BlockShift();
get_default_volume_name(deviceSize, cookie->name, sizeof(cookie->name));
}
*_cookie = cookie;
@ -123,23 +125,23 @@ exfat_identify_partition(int fd, partition_data* _partition, void** _cookie)
static status_t
exfat_scan_partition(int fd, partition_data* _partition, void* _cookie)
exfat_scan_partition(int fd, partition_data* partition, void* _cookie)
{
identify_cookie* cookie = (identify_cookie*)_cookie;
_partition->status = B_PARTITION_VALID;
_partition->flags |= B_PARTITION_FILE_SYSTEM;
_partition->content_size = cookie->super_block.NumBlocks()
partition->status = B_PARTITION_VALID;
partition->flags |= B_PARTITION_FILE_SYSTEM;
partition->content_size = cookie->super_block.NumBlocks()
<< cookie->super_block.BlockShift();
_partition->block_size = 1 << cookie->super_block.BlockShift();
_partition->content_name = strdup(cookie->name);
partition->block_size = 1 << cookie->super_block.BlockShift();
partition->content_name = strdup(cookie->name);
return _partition->content_name != NULL ? B_OK : B_NO_MEMORY;
return partition->content_name != NULL ? B_OK : B_NO_MEMORY;
}
static void
exfat_free_identify_partition_cookie(partition_data* _partition, void* _cookie)
exfat_free_identify_partition_cookie(partition_data* partition, void* _cookie)
{
delete (identify_cookie*)_cookie;
}