#include "support.h"
#ifndef FS_SHELL
#include <dirent.h>
#include <malloc.h>
#include <NodeMonitor.h>
#include <SupportDefs.h>
#include <AutoDeleter.h>
#include <file_systems/mime_ext_table.h>
#include <kernel.h>
#include <real_time_clock.h>
#include <util/AutoLock.h>
#else
#include <fssh_kernel_priv.h>
#endif
#include "debug.h"
#include "dosfs.h"
#include "vcache.h"
extern struct iconv_functions* msdosfs_iconv;
struct emptyDir gDirTemplate = {
{
{'.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
ATTR_DIRECTORY,
0,
0, {0, 0}, {0, 0},
{0, 0},
{0, 0},
{210, 4}, {210, 4},
{0, 0},
{0, 0, 0, 0}
},
{
{'.', '.', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
ATTR_DIRECTORY,
0,
0, {0, 0}, {0, 0},
{0, 0},
{0, 0},
{210, 4}, {210, 4},
{0, 0},
{0, 0, 0, 0}
}};
ComponentName::ComponentName(u_int64_t flags, ucred* cred, enum nameiop nameiop, int lkflags,
const char* nameptr)
{
fData.cn_flags = flags;
fData.cn_cred = cred;
fData.cn_nameiop = nameiop;
fData.cn_lkflags = lkflags;
fData.cn_nameptr = strdup(nameptr);
fData.cn_namelen = strlen(fData.cn_nameptr);
}
ComponentName::~ComponentName()
{
free(fData.cn_nameptr);
}
struct componentname*
ComponentName::Data()
{
return &fData;
}
bool
is_filename_legal(const char* name)
{
if (name == NULL)
return false;
const char illegal[] = "\\/:*?\"<>|";
uint32 len = strlen(name);
if (len <= 0)
return false;
if (strrchr(name, '.') == name)
return false;
bool hasContent = false;
for (uint32 i = 0; i < len && hasContent == false; i++) {
if (name[i] != ' ' && name[i] != '.')
hasContent = true;
}
if (hasContent == false)
return false;
for (uint32 i = 0; i < len; i++) {
if (name[i] & 0x80)
continue;
if (strchr(illegal, name[i]))
return false;
if (static_cast<unsigned char>(name[i]) < 32)
return false;
}
return true;
}
bool
is_shortname_legal(const u_char* name)
{
const char* device_names[] = {"CON ", "PRN ", "AUX ", "NUL ",
"COM0 ", "COM1 ", "COM2 ", "COM3 ", "COM4 ", "COM5 ",
"COM6 ", "COM7 ", "COM8 ", "COM9 ", "LPT0 ", "LPT1 ",
"LPT2 ", "LPT3 ", "LPT4 ", "LPT5 ", "LPT6 ", "LPT7 ",
"LPT8 ", "LPT9 ", NULL};
for (uint32 i = 0; device_names[i]; i++) {
if (memcmp(name, device_names[i], 8) == 0)
return false;
}
return true;
}
status_t
label_to_fat(char* label)
{
if (label[0] == 0) {
INFORM("label_to_fat: empty name\n");
return B_BAD_VALUE;
}
if (label[0] == ' ') {
INFORM("label_to_fat: vol name starts with space\n");
return B_BAD_VALUE;
}
uint32 length = strlen(label);
if (length > LABEL_LENGTH)
return B_NAME_TOO_LONG;
for (uint32 i = 0; i < length; i++) {
char c = label[i];
if (c >= 'a' && c <= 'z')
c+= 'A' - 'a';
if (strchr(sAcceptable, c) || c == ' ') {
label[i] = c;
} else {
INFORM("label_to_fat: vol name contains illegal char (%c at index %" B_PRIu32
")\n", label[i], i);
return B_BAD_VALUE;
}
}
for (uint32 i = length; i < LABEL_LENGTH; i++)
label[i] = ' ';
PRINT("label_to_fat: converted to [%11.11s].\n", label);
return B_OK;
}
void
label_from_fat(char* name)
{
int i;
for (i = 10; i > 0; i--) {
if (name[i] != ' ')
break;
}
name[i + 1] = 0;
for (; i >= 0; i--) {
if (name[i] >= 'A' && name[i] <= 'Z')
name[i] += 'a' - 'A';
}
}
status_t
read_label(const msdosfsmount* volume, int fd, const uint8* buffer, char* label)
{
*label = '\0';
bool fat32 = FAT32(volume);
uint8 check = fat32 == true ? 0x42 : 0x26;
uint8 offset = fat32 == true ? 0x47 : 0x2b;
if (buffer[check] == EXBOOTSIG && memcmp(buffer + offset, " ", LABEL_LENGTH) != 0) {
memcpy(label, buffer + offset, LABEL_LENGTH);
label_from_fat(label);
} else {
return B_OK;
}
if (volume->pm_mountp != NULL)
volume->pm_mountp->mnt_volentry = -1;
int32 rootDirBytes;
if (fat32 == true)
rootDirBytes = volume->pm_bpcluster;
else
rootDirBytes = (volume->pm_rootdirsize / volume->pm_BlkPerSec) * volume->pm_BytesPerSec;
uint8* rootDirBuffer = static_cast<uint8*>(calloc(rootDirBytes, sizeof(char)));
daddr_t rootDirBlock = volume->pm_rootdirblk;
if (fat32 == true)
rootDirBlock = cntobn(volume, rootDirBlock);
off_t rootDirPos = de_bn2off(volume, rootDirBlock);
int32 bytesRead = read_pos(fd, rootDirPos, rootDirBuffer, rootDirBytes);
if (bytesRead != rootDirBytes) {
free(rootDirBuffer);
RETURN_ERROR(B_IO_ERROR);
}
uint32 direntrySize = sizeof(struct direntry);
uint32 rootDirEntries = rootDirBytes / direntrySize;
struct direntry* direntry = NULL;
for (uint32 i = 0;
i < rootDirEntries && (direntry == NULL || direntry->deName[0] != SLOT_EMPTY); ++i) {
direntry = reinterpret_cast<struct direntry*>(rootDirBuffer + direntrySize * i);
if (direntry->deName[0] != SLOT_DELETED && direntry->deAttributes != ATTR_WIN95
&& (direntry->deAttributes & ATTR_VOLUME) != 0) {
memcpy(label, direntry->deName, 11);
label_from_fat(label);
PRINT("found volume label in root directory: %s at i = %" B_PRIu32 "\n", label, i);
if (volume->pm_mountp != NULL)
volume->pm_mountp->mnt_volentry = i;
break;
}
}
free(rootDirBuffer);
return B_OK;
}
status_t
check_bootsector(const uint8* bootsector, FatType& _type, bool& _dos33)
{
if (bootsector[0x1fe] != BOOTSIG0 || bootsector[0x1ff] != BOOTSIG1)
return B_BAD_VALUE;
if (!memcmp((uint8*) bootsector + 3, "NTFS ", 8)
|| !memcmp((uint8*) bootsector + 3, "HPFS ", 8)) {
return B_BAD_VALUE;
}
uint32 bytesPerSector = read16(bootsector, 0Xb);
if (bytesPerSector != 512 && bytesPerSector != 1024 && bytesPerSector != 2048
&& bytesPerSector != 4096) {
return B_BAD_VALUE;
}
uint32 sectorsPerCluster = bootsector[0xd];
if (sectorsPerCluster == 0)
return B_BAD_VALUE;
uint32 rootDirEntries = read16(bootsector, 0x11);
uint32 rootDirSectors = ((rootDirEntries * 32) + (bytesPerSector - 1)) / bytesPerSector;
uint32 sectorsPerFat = read16(bootsector, 0x16);
if (sectorsPerFat == 0)
sectorsPerFat = read32(bootsector, 0x24);
uint32 totalSectors = read16(bootsector, 0x13);
if (totalSectors == 0)
totalSectors = read32(bootsector, 0x20);
uint32 reservedSectors = read16(bootsector, 0xe);
uint32 fatCount = bootsector[0x10];
uint32 dataSectors
= totalSectors - (reservedSectors + (fatCount * sectorsPerFat) + rootDirSectors);
uint32 clusterCount = dataSectors / sectorsPerCluster;
if (clusterCount < 4085)
_type = fat12;
else if (clusterCount < 65525)
_type = fat16;
else
_type = fat32;
if (_type != fat32 && bootsector[0x26] != EXBOOTSIG)
_dos33 = true;
else
_dos33 = false;
return B_OK;
}
status_t
parse_bpb(msdosfsmount* volume, const bootsector* bootsector, bool dos33)
{
status_t status = B_OK;
const universal_byte_bpb* bpb
= reinterpret_cast<const universal_byte_bpb*>(bootsector->bs33.bsBPB);
volume->pm_BytesPerSec = getushort(bpb->bpbBytesPerSec);
volume->pm_bpb.bpbSecPerClust = bpb->bpbSecPerClust;
volume->pm_ResSectors = getushort(bpb->bpbResSectors);
volume->pm_FATs = bpb->bpbFATs;
volume->pm_RootDirEnts = getushort(bpb->bpbRootDirEnts);
volume->pm_Sectors = getushort(bpb->bpbSectors);
volume->pm_Media = bpb->bpbMedia;
volume->pm_FATsecs = volume->pm_bpb.bpbFATsecs = getushort(bpb->bpbFATsecs);
volume->pm_SecPerTrack = getushort(bpb->bpbSecPerTrack);
volume->pm_Heads = getushort(bpb->bpbHeads);
if (volume->pm_BytesPerSec != 0x200 && volume->pm_BytesPerSec != 0x400
&& volume->pm_BytesPerSec != 0x800 && volume->pm_BytesPerSec != 0x1000) {
INFORM("invalid bytes per sector (%d)\n", volume->pm_BytesPerSec);
status = B_BAD_VALUE;
} else if (volume->pm_BytesPerSec < DEV_BSIZE) {
INFORM("invalid bytes per sector (%d)\n", volume->pm_BytesPerSec);
status = B_BAD_VALUE;
} else if (volume->pm_bpb.bpbSecPerClust != 1 && volume->pm_bpb.bpbSecPerClust != 2
&& volume->pm_bpb.bpbSecPerClust != 4 && volume->pm_bpb.bpbSecPerClust != 8
&& volume->pm_bpb.bpbSecPerClust != 16 && volume->pm_bpb.bpbSecPerClust != 32
&& volume->pm_bpb.bpbSecPerClust != 64 && volume->pm_bpb.bpbSecPerClust != 128) {
INFORM("invalid sectors per cluster (%" B_PRIu8 ")\n", volume->pm_bpb.bpbSecPerClust);
status = B_BAD_VALUE;
} else if (volume->pm_BytesPerSec * volume->pm_bpb.bpbSecPerClust > 0x10000) {
INFORM("invalid bytes per cluster (%" B_PRIu16 ")\n",
volume->pm_BytesPerSec * volume->pm_bpb.bpbSecPerClust);
status = B_BAD_VALUE;
} else if (volume->pm_FATs == 0 || volume->pm_FATs > 8) {
INFORM("unreasonable fat count (%" B_PRIu8 ")\n", volume->pm_FATs);
status = B_BAD_VALUE;
} else if (volume->pm_Media != 0xf0 && volume->pm_Media < 0xf8) {
INFORM("invalid media descriptor byte\n");
status = B_BAD_VALUE;
}
if (status != B_OK)
return status;
if (dos33 == true && (FAT12(volume) != 0 || FAT16(volume) != 0)) {
const struct byte_bpb33* b33 = reinterpret_cast<const byte_bpb33*>(bootsector->bs33.bsBPB);
if (volume->pm_Sectors == 0) {
INFORM("sector count missing\n");
status = B_BAD_VALUE;
} else {
volume->pm_HugeSectors = volume->pm_Sectors;
volume->pm_HiddenSects = getushort(b33->bpbHiddenSecs);
volume->pm_flags |= MSDOSFS_FATMIRROR;
}
} else if (FAT12(volume) != 0 || FAT16(volume) != 0) {
const struct byte_bpb50* b50 = reinterpret_cast<const byte_bpb50*>(bootsector->bs50.bsBPB);
if (volume->pm_Sectors == 0)
volume->pm_HugeSectors = getulong(b50->bpbHugeSectors);
else
volume->pm_HugeSectors = volume->pm_Sectors;
volume->pm_HiddenSects = getulong(b50->bpbHiddenSecs);
volume->pm_flags |= MSDOSFS_FATMIRROR;
if (FAT16(volume) && volume->pm_mountp != NULL)
fix_zip(b50, volume);
} else if (FAT32(volume) != 0) {
const struct byte_bpb710* b710
= reinterpret_cast<const byte_bpb710*>(bootsector->bs710.bsBPB);
volume->pm_HiddenSects = getulong(b710->bpbHiddenSecs);
volume->pm_HugeSectors = getulong(b710->bpbHugeSectors);
volume->pm_FATsecs = getulong(b710->bpbBigFATsecs);
if ((getushort(b710->bpbExtFlags) & FATMIRROR) != 0)
volume->pm_curfat = getushort(b710->bpbExtFlags) & FATNUM;
else
volume->pm_flags |= MSDOSFS_FATMIRROR;
volume->pm_rootdirblk = getulong(b710->bpbRootClust);
volume->pm_fsinfo = getushort(b710->bpbFSInfo);
} else {
status = B_UNSUPPORTED;
}
if (status != B_OK)
return status;
if (FAT12(volume) || FAT16(volume)) {
if ((volume->pm_RootDirEnts * 32) % volume->pm_BytesPerSec != 0) {
INFORM("invalid number of root dir entries\n");
status = B_BAD_VALUE;
}
} else if (FAT32(volume)) {
const struct byte_bpb710* b710
= reinterpret_cast<const byte_bpb710*>(bootsector->bs710.bsBPB);
if (getushort(b710->bpbSectors) != 0 || getushort(b710->bpbFSVers) != 0
|| getushort(b710->bpbRootDirEnts) != 0 || getushort(b710->bpbFATsecs) != 0) {
INFORM("bad FAT32 filesystem\n");
status = B_BAD_VALUE;
}
}
if ((off_t) volume->pm_HugeSectors * volume->pm_BytesPerSec < volume->pm_HugeSectors) {
INFORM("sectors overflow\n");
status = B_BAD_VALUE;
}
if (volume->pm_mountp != NULL) {
if (static_cast<off_t>(volume->pm_HugeSectors) * volume->pm_BytesPerSec
> volume->pm_dev->si_mediasize) {
INFORM("sectors past end of vol: %u sectors on a %" B_PRIdOFF "-byte volume\n",
volume->pm_HugeSectors, volume->pm_dev->si_mediasize);
status = B_BAD_VALUE;
}
}
return status;
}
void
fix_zip(const byte_bpb50* bpb, msdosfsmount* volume)
{
device_geometry* geo = volume->pm_dev->si_geometry;
if (geo != NULL) {
unsigned char bogusZipData[] = {0x00, 0x02, 0x04, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00,
0xf8, 0xc0, 0x00, 0x20, 0x00, 0x40, 0x00, 0x20, 0x00, 0x00};
if (memcmp(bpb, bogusZipData, sizeof(bogusZipData)) == 0 && volume->pm_HugeSectors == 196576
&& (static_cast<off_t>(geo->sectors_per_track) * static_cast<off_t>(geo->cylinder_count)
* static_cast<off_t>(geo->head_count))
== 196192) {
volume->pm_HugeSectors = 196192;
}
}
return;
}
status_t
read_fsinfo(msdosfsmount* volume, const vnode* devNode)
{
status_t status = B_OK;
if (FAT32(volume) != 0) {
const uint8* buffer;
const struct fsinfo* fsInfo;
off_t cachedBlock = BLOCK_TO_SECTOR(volume, volume->pm_fsinfo);
status = block_cache_get_etc(volume->pm_mountp->mnt_cache, cachedBlock,
reinterpret_cast<const void**>(&buffer));
if (status != B_OK)
RETURN_ERROR(status);
fsInfo = reinterpret_cast<const fsinfo*>(buffer);
if (memcmp(fsInfo->fsisig1, "RRaA", 4) == 0 && memcmp(fsInfo->fsisig2, "rrAa", 4) == 0
&& memcmp(fsInfo->fsisig3, "\0\0\125\252", 4) == 0) {
volume->pm_nxtfree = getulong(fsInfo->fsinxtfree);
if (volume->pm_nxtfree > volume->pm_maxcluster)
volume->pm_nxtfree = CLUST_FIRST;
} else {
INFORM("fsinfo block has invalid magic number\n");
status = B_BAD_VALUE;
volume->pm_fsinfo = 0;
}
block_cache_put(volume->pm_mountp->mnt_cache, cachedBlock);
}
if (volume->pm_nxtfree < CLUST_FIRST)
volume->pm_nxtfree = CLUST_FIRST;
return status;
}
status_t
write_fsinfo(msdosfsmount* volume)
{
if (volume->pm_fsinfo == 0 || FAT32(volume) == false || (volume->pm_flags & MSDOSFS_FSIMOD) == 0
|| (volume->pm_flags & MSDOSFSMNT_RONLY) != 0) {
return B_OK;
}
off_t cachedBlock = BLOCK_TO_SECTOR(volume, volume->pm_fsinfo);
void* buffer = block_cache_get_writable(volume->pm_mountp->mnt_cache, cachedBlock, -1);
if (buffer == NULL)
RETURN_ERROR(B_ERROR);
struct fsinfo* fsInfo = reinterpret_cast<struct fsinfo*>(buffer);
if (memcmp(fsInfo->fsisig1, "RRaA", 4) != 0 || memcmp(fsInfo->fsisig2, "rrAa", 4) != 0
|| memcmp(fsInfo->fsisig3, "\0\0\125\252", 4) != 0) {
block_cache_set_dirty(volume->pm_mountp->mnt_cache, cachedBlock, false, -1);
block_cache_put(volume->pm_mountp->mnt_cache, cachedBlock);
RETURN_ERROR(B_ERROR);
}
putulong(fsInfo->fsinfree, volume->pm_freeclustercount);
putulong(fsInfo->fsinxtfree, volume->pm_nxtfree);
volume->pm_flags &= ~MSDOSFS_FSIMOD;
block_cache_put(volume->pm_mountp->mnt_cache, cachedBlock);
return B_OK;
}
status_t
check_fat(const msdosfsmount* volume)
{
uint32 bytesPerSec = volume->pm_BytesPerSec;
uint32 fatSectors = volume->pm_FATsecs / volume->pm_BlkPerSec;
uint8 fatBuffer[bytesPerSec];
uint8 mirrorBuffer[bytesPerSec];
uint32 checkSectors = fatSectors > 4096 ? 1 : fatSectors;
PRINT("check_fat checking %" B_PRIu32 " sectors\n", checkSectors);
for (uint32 i = 0; i < checkSectors; ++i) {
uint32 resSectors = volume->pm_ResSectors;
off_t position = bytesPerSec * (resSectors + volume->pm_curfat * fatSectors + i);
ssize_t bytes_read
= read_pos(volume->pm_dev->si_fd, position, reinterpret_cast<void*>(fatBuffer), bytesPerSec);
if (bytes_read != static_cast<ssize_t>(bytesPerSec))
RETURN_ERROR(B_IO_ERROR);
for (uint32 j = 0; j < volume->pm_FATs; ++j) {
if (j == volume->pm_curfat)
continue;
position = bytesPerSec * (resSectors + fatSectors * j + i);
bytes_read = read_pos(volume->pm_dev->si_fd, position,
reinterpret_cast<void*>(mirrorBuffer), bytesPerSec);
if (bytes_read != static_cast<ssize_t>(bytesPerSec))
RETURN_ERROR(B_IO_ERROR);
if (i == 0 && mirrorBuffer[0] != volume->pm_Media) {
INFORM("dosfs error: media descriptor mismatch in fat # "
"%" B_PRIu32 " (%" B_PRIu8 " != %" B_PRIu8 ")\n",
j, mirrorBuffer[0], volume->pm_Media);
return B_BAD_VALUE;
}
if (memcmp(fatBuffer, mirrorBuffer, bytesPerSec)) {
INFORM("FAT %" B_PRIu32 " doesn't match active FAT (%u) on %s.\n"
"Install dosfstools and use fsck.fat to inspect %s.\n",
j, volume->pm_curfat, volume->pm_dev->si_device, volume->pm_dev->si_device);
}
}
}
return B_OK;
}
uint32
get_nth_fat_entry(msdosfsmount* fatVolume, uint32 cluster, uint32 n)
{
int status = 0;
ReadLocker locker(fatVolume->pm_fatlock.haikuRW);
u_long resultCluster = static_cast<u_long>(cluster);
while (n--) {
status = fatentry(FAT_GET, fatVolume, resultCluster, &resultCluster, 0);
if (status != 0) {
REPORT_ERROR(B_FROM_POSIX_ERROR(status));
break;
}
ASSERT(IS_DATA_CLUSTER(resultCluster));
}
return static_cast<uint32>(resultCluster);
}
status_t
init_csi(msdosfsmount* fatVolume, uint32 cluster, uint32 sector, struct csi* csi)
{
int ret;
if ((ret = validate_cs(fatVolume, cluster, sector)) != B_OK)
return ret;
csi->fatVolume = fatVolume;
csi->cluster = cluster;
csi->sector = sector;
return B_OK;
}
status_t
validate_cs(msdosfsmount* fatVolume, uint32 cluster, uint32 sector)
{
if (cluster == MSDOSFSROOT) {
if (sector >= fatVolume->pm_rootdirsize / fatVolume->pm_BlkPerSec)
return B_ERROR;
return B_OK;
}
if (sector >= SECTORS_PER_CLUSTER(fatVolume)) {
INFORM("validate_cs: invalid sector (%" B_PRIu32 ")\n", sector);
return B_ERROR;
}
if (!IS_DATA_CLUSTER(cluster)) {
INFORM("validate_cs: cluster %" B_PRIu32 " compared to max %lu\n", cluster,
fatVolume->pm_maxcluster);
return B_ERROR;
}
return B_OK;
}
off_t
fs_sector(struct csi* csi)
{
ASSERT(validate_cs(csi->fatVolume, csi->cluster, csi->sector) == 0);
if (csi->cluster == MSDOSFSROOT) {
return static_cast<off_t>(csi->fatVolume->pm_rootdirblk) / csi->fatVolume->pm_BlkPerSec
+ csi->sector;
}
off_t clusterStart
= cntobn(csi->fatVolume, static_cast<off_t>(csi->cluster)) / csi->fatVolume->pm_BlkPerSec;
return clusterStart + csi->sector;
}
status_t
iter_csi(struct csi* csi, int sectors)
{
if (csi->sector == 0xffff)
return B_ERROR;
if (sectors < 0)
return B_BAD_VALUE;
if (sectors == 0)
return B_OK;
if (csi->cluster == MSDOSFSROOT) {
csi->sector += sectors;
if (csi->sector < csi->fatVolume->pm_rootdirsize / csi->fatVolume->pm_BlkPerSec)
return B_OK;
} else {
u_long secPerClust = SECTORS_PER_CLUSTER(csi->fatVolume);
csi->sector += sectors;
if (csi->sector < secPerClust)
return B_OK;
csi->cluster = get_nth_fat_entry(csi->fatVolume, csi->cluster, csi->sector / secPerClust);
if (MSDOSFSEOF(csi->fatVolume, csi->cluster)) {
csi->sector = 0xffff;
return B_ERROR;
}
if (vIS_DATA_CLUSTER(csi->fatVolume, csi->cluster)) {
csi->sector %= SECTORS_PER_CLUSTER(csi->fatVolume);
return B_OK;
}
}
csi->sector = 0xffff;
return B_ERROR;
}
NodePutter::NodePutter()
:
fNode(NULL)
{
}
NodePutter::NodePutter(vnode* node)
:
fNode(node)
{
}
NodePutter::~NodePutter()
{
Put();
}
void
NodePutter::SetTo(vnode* node)
{
fNode = node;
}
void
NodePutter::Put()
{
if (fNode != NULL) {
fs_volume* volume = fNode->v_mount->mnt_fsvolume;
denode* fatNode = reinterpret_cast<denode*>(fNode->v_data);
ino_t ino = static_cast<ino_t>(fatNode->de_inode);
status_t status = put_vnode(volume, ino);
if (status != B_OK)
REPORT_ERROR(status);
fNode = NULL;
}
}
status_t
assign_inode(mount* volume, ino_t* inode)
{
ino_t location = *inode;
status_t result = B_OK;
if (*inode == root_inode(reinterpret_cast<msdosfsmount*>(volume->mnt_data)))
return B_OK;
ino_t finalInode = 0;
result = vcache_loc_to_vnid(volume, location, &finalInode);
if (result == ENOENT) {
if (find_vnid_in_vcache(volume, location) == B_OK) {
finalInode = generate_unique_vnid(volume);
if ((result = add_to_vcache(volume, finalInode, location)) < 0)
RETURN_ERROR(result);
} else {
finalInode = location;
if ((result = add_to_vcache(volume, finalInode, location)) < 0)
RETURN_ERROR(result);
}
} else if (result != B_OK) {
RETURN_ERROR(result);
}
PRINT("assign_inode in: %" B_PRIdINO ", out: %" B_PRIdINO "\n", *inode, finalInode);
*inode = finalInode;
return B_OK;
}
status_t
assign_inode_and_get(mount* bsdVolume, daddr_t cluster, u_long offset, vnode** bsdNode)
{
msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
ino_t ino = DETOI(fatVolume, cluster, offset);
status_t status = assign_inode(bsdVolume, &ino);
if (status != B_OK)
RETURN_ERROR(B_ENTRY_NOT_FOUND);
fs_volume* fsVolume = bsdVolume->mnt_fsvolume;
status = get_vnode(fsVolume, ino, reinterpret_cast<void**>(bsdNode));
if (status != B_OK)
RETURN_ERROR(B_ENTRY_NOT_FOUND);
return B_OK;
}
status_t
get_location(mount* bsdVolume, ino_t vnid, u_long* dirclust, u_long* diroffset)
{
msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(bsdVolume->mnt_data);
ino_t location = -1;
status_t status = vcache_vnid_to_loc(bsdVolume, vnid, &location);
if (status != B_OK)
return status;
if (dirclust != NULL && diroffset != NULL) {
if (static_cast<unsigned long>(location) < fatVolume->pm_RootDirEnts) {
*dirclust = MSDOSFSROOT;
*diroffset = location << 5;
} else {
location -= fatVolume->pm_RootDirEnts;
location <<= 5;
*dirclust = (location / fatVolume->pm_bpcluster) + 2;
*diroffset = location % fatVolume->pm_bpcluster;
}
}
return B_OK;
}
bool
node_exists(mount* bsdVolume, uint64_t inode)
{
bool constructed = false;
vcache_get_constructed(bsdVolume, inode, &constructed);
return constructed;
}
void
timestamp_local(timespec* tsp)
{
bigtime_t usecs = real_time_clock_usecs();
tsp->tv_sec = (usecs / 1000000LL);
tsp->tv_nsec = (usecs - tsp->tv_sec * 1000000LL) * 1000LL;
return;
}
void
local_to_GMT(const timespec* tspLocal, timespec* tspGMT)
{
*tspGMT = *tspLocal;
int32 offset = 0;
#ifdef _KERNEL_MODE
offset = static_cast<int32>(get_timezone_offset());
#elif defined USER
time_t localTime;
time(&localTime);
struct tm localTm;
localtime_r(&localTime, &localTm);
offset = localTm.tm_gmtoff;
#endif
tspGMT->tv_sec -= offset;
return;
}
status_t
slist_insert_buf(vnode* deviceNode, size_t size)
{
buf_list* list = NULL;
uint32* count = NULL;
msdosfsmount* fatVolume
= reinterpret_cast<msdosfsmount*>(deviceNode->v_rdev->si_mountpt->mnt_data);
if (size == 0) {
list = &deviceNode->v_bufobj.bo_emptybufs;
count = &deviceNode->v_bufobj.bo_empties;
} else if (size == fatVolume->pm_fatblocksize) {
list = &deviceNode->v_bufobj.bo_fatbufs;
count = &deviceNode->v_bufobj.bo_fatblocks;
} else if (size == fatVolume->pm_bpcluster) {
list = &deviceNode->v_bufobj.bo_clusterbufs;
count = &deviceNode->v_bufobj.bo_clusters;
} else {
return B_UNSUPPORTED;
}
buf* newBuf = reinterpret_cast<buf*>(calloc(1, sizeof(buf)));
if (newBuf == NULL)
return B_NO_MEMORY;
if (size != 0) {
newBuf->b_data = reinterpret_cast<caddr_t>(calloc(size, sizeof(char)));
if (newBuf->b_data == NULL) {
free(newBuf);
return B_NO_MEMORY;
}
}
newBuf->b_bufsize = size;
SLIST_INSERT_HEAD(list, newBuf, link);
(*count)++;
return B_OK;
}
status_t
fill_gap_with_zeros(vnode* bsdNode, off_t pos, off_t newSize)
{
while (pos < newSize) {
size_t size;
if (newSize > pos + 1024 * 1024 * 1024)
size = 1024 * 1024 * 1024;
else
size = newSize - pos;
status_t status = file_cache_write(bsdNode->v_cache, NULL, pos, NULL, &size);
if (status < B_OK)
return status;
pos += size;
}
return B_OK;
}
status_t
sync_clusters(vnode* bsdNode)
{
mount* bsdVolume = bsdNode->v_mount;
denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
msdosfsmount* fatVolume = fatNode->de_pmp;
status_t status = B_OK;
ASSERT(bsdNode->v_type == VDIR);
u_long cluster = fatNode->de_dirclust;
if (cluster == MSDOSFSROOT) {
off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, fatVolume->pm_rootdirblk);
size_t numBlocks =
HOWMANY(fatVolume->pm_rootdirsize * DEV_BSIZE, fatVolume->pm_BytesPerSec);
status = block_cache_sync_etc(bsdVolume->mnt_cache, cachedBlock, numBlocks);
} else {
status_t fatStatus = B_OK;
while ((IS_DATA_CLUSTER(cluster)) && status == B_OK && fatStatus == B_OK) {
off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, cntobn(fatVolume, cluster));
status = block_cache_sync_etc(bsdVolume->mnt_cache, cachedBlock,
SECTORS_PER_CLUSTER(fatVolume));
fatStatus = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0));
}
if (fatStatus != B_OK)
REPORT_ERROR(fatStatus);
}
RETURN_ERROR(status);
}
status_t
discard_clusters(vnode* bsdNode, off_t newLength)
{
mount* bsdVolume = bsdNode->v_mount;
denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
msdosfsmount* fatVolume = fatNode->de_pmp;
status_t status = B_OK;
ASSERT(bsdNode->v_type == VDIR);
u_long cluster = fatNode->de_dirclust;
for (uint32 skip = HOWMANY(newLength, fatVolume->pm_bpcluster); skip > 0 && status == B_OK;
skip--) {
status = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0));
}
while ((IS_DATA_CLUSTER(cluster)) && status == B_OK) {
off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, cntobn(fatVolume, cluster));
block_cache_discard(bsdVolume->mnt_cache, cachedBlock, SECTORS_PER_CLUSTER(fatVolume));
status = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0));
}
RETURN_ERROR(status);
}
status_t
check_access_permissions_internal(int accessMode, mode_t mode, gid_t nodeGroupID, uid_t nodeUserID)
{
int userPermissions = (mode & S_IRWXU) >> 6;
int groupPermissions = (mode & S_IRWXG) >> 3;
int otherPermissions = mode & S_IRWXO;
int permissions = 0;
uid_t uid = geteuid();
if (uid == 0) {
permissions = userPermissions | groupPermissions | otherPermissions | S_IROTH | S_IWOTH;
if (S_ISDIR(mode))
permissions |= S_IXOTH;
} else if (uid == nodeUserID) {
permissions = userPermissions;
} else {
permissions = otherPermissions;
}
return (accessMode & ~permissions) == 0 ? B_OK : B_PERMISSION_DENIED;
}
void
mode_bits(const vnode* bsdNode, mode_t* mode)
{
denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
msdosfsmount* fatVolume = fatNode->de_pmp;
*mode = S_IRUSR | S_IRGRP | S_IROTH;
if ((fatNode->de_Attributes & ATTR_READONLY) == 0)
*mode |= S_IWUSR;
if (bsdNode->v_type == VDIR
|| (bsdNode->v_type == VREG && bsdNode->v_mime != NULL
&& strcmp(bsdNode->v_mime, "application/octet-stream") == 0)) {
*mode |= S_IXUSR | S_IXGRP | S_IXOTH;
}
*mode &= (bsdNode->v_type == VDIR) ? fatVolume->pm_dirmask : fatVolume->pm_mask;
return;
}
status_t
set_mime_type(vnode* bsdNode, bool update)
{
#ifndef FS_SHELL
mount* bsdVolume = reinterpret_cast<mount*>(bsdNode->v_mount);
denode* fatNode = reinterpret_cast<denode*>(bsdNode->v_data);
msdosfsmount* fatVolume = reinterpret_cast<msdosfsmount*>(fatNode->de_pmp);
if (bsdNode->v_type == VREG) {
char unixShortname[SHORTNAME_CSTRING + 1];
dos2unixfn(fatNode->de_Name, reinterpret_cast<u_char*>(unixShortname), 0, fatVolume);
set_mime(&bsdNode->v_mime, unixShortname);
notify_attribute_changed(bsdVolume->mnt_fsvolume->id, bsdNode->v_parent, fatNode->de_inode,
"BEOS:TYPE", update ? B_ATTR_CHANGED : B_ATTR_CREATED);
}
#endif
return B_OK;
}
status_t
iconv_init(msdosfsmount* fatVolume, const char* oemPreference)
{
fatVolume->pm_u2w = NULL;
fatVolume->pm_w2u = NULL;
fatVolume->pm_u2d = NULL;
fatVolume->pm_d2u = NULL;
#ifndef USER
return B_OK;
#else
if ((fatVolume->pm_flags & MSDOSFSMNT_KICONV) == 0 || strcmp(oemPreference, "") == 0)
return B_OK;
msdosfs_iconv = new(std::nothrow) iconv_functions;
if (msdosfs_iconv == NULL)
RETURN_ERROR(B_NO_MEMORY);
ObjectDeleter<iconv_functions> deleter(msdosfs_iconv);
msdosfs_iconv->open = fat_iconv_open;
msdosfs_iconv->close = fat_iconv_close;
msdosfs_iconv->conv = iconv_conv;
msdosfs_iconv->convchr = iconv_convchr;
msdosfs_iconv->convchr_case = iconv_convchr_case;
PRINT("setting code page to %s\n", oemPreference);
const char* haiku = "UTF-8";
const char* windows = "UCS-2";
const char* dos = oemPreference;
status_t status = B_OK;
status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(windows, haiku, &fatVolume->pm_u2w));
if (status != B_OK)
RETURN_ERROR(errno);
status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(haiku, windows, &fatVolume->pm_w2u));
if (status != B_OK)
RETURN_ERROR(errno);
status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(dos, haiku, &fatVolume->pm_u2d));
if (status != B_OK)
RETURN_ERROR(errno);
status = B_FROM_POSIX_ERROR(msdosfs_iconv->open(haiku, dos, &fatVolume->pm_d2u));
if (status != B_OK)
RETURN_ERROR(errno);
deleter.Detach();
return B_OK;
#endif
}