nuttx-apps/fsutils/mkfatfs/writefat.c

653 lines
18 KiB
C
Raw Normal View History

/****************************************************************************
* apps/fsutils/mkfatfs/writefat.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <inttypes.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <unistd.h>
#include <nuttx/fs/fat.h>
#include "fsutils/mkfatfs.h"
#include "fat32.h"
#include "mkfatfs.h"
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: mkfatfs_devwrite
*
* Description:
* Write the content of the dedicate sector buffer beginning to the
* specified sector
*
* Input:
* fmt - User specified format parameters
* var - Other format parameters that are not user specifiable
*
* Return:
* None; caller is responsible for providing valid parameters.
*
****************************************************************************/
static int mkfatfs_devwrite(FAR const struct fat_format_s *fmt,
FAR const struct fat_var_s *var, off_t sector)
{
ssize_t nwritten;
off_t seekpos;
off_t fpos;
int ret;
/* Convert the sector number to a byte offset */
2022-08-22 15:17:00 +02:00
if (sector < 0 || sector >= (off_t)fmt->ff_nsectors)
{
ferr("sector out of range: %ju\n", (intmax_t)sector);
return -ESPIPE;
}
fpos = sector << var->fv_sectshift;
/* Seek to that offset */
seekpos = lseek(var->fv_fd, fpos, SEEK_SET);
if (seekpos == (off_t)-1)
{
ret = -errno;
ferr("ERROR: lseek to %jd failed: %d\n", (intmax_t)fpos, ret);
return ret;
}
else if (seekpos != fpos)
{
ferr("ERROR: lseek failed: %ju vs %ju\n",
(intmax_t)seekpos, (intmax_t)fpos);
return -EINVAL;
}
/* Write the sector to that offset. Partial writes are not expected. */
nwritten = write(var->fv_fd, var->fv_sect, var->fv_sectorsize);
if (nwritten < 0)
{
ret = -errno;
ferr("ERROR: write failed: size=%" PRIu32 " pos=%jd error=%d\n",
var->fv_sectorsize, (intmax_t)fpos, ret);
return ret;
}
else if (nwritten != (ssize_t)var->fv_sectorsize)
{
ferr("ERROR: Partial write: size=%" PRIu32 " written=%zd\n",
var->fv_sectorsize, nwritten);
return -ENODATA;
}
return OK;
}
/****************************************************************************
* Name: mkfatfs_initmbr
*
* Description:
* Initialize the sector image of a masterbood record
*
* Input:
* fmt - User specified format parameters
* var - Other format parameters that are not user specifiable
*
* Return:
* None; caller is responsible for providing valid parameters.
*
****************************************************************************/
static inline void mkfatfs_initmbr(FAR struct fat_format_s *fmt,
FAR struct fat_var_s *var)
{
memset(var->fv_sect, 0, var->fv_sectorsize);
/* 3@0: Jump instruction to boot code */
memcpy(&var->fv_sect[MBR_JUMP], var->fv_jump, 3);
/* 8@3: Usually "MSWIN4.1" */
memcpy(&var->fv_sect[MBR_OEMNAME], "NUTTX ", 8);
/* 2@11: Bytes per sector: 512, 1024, 2048, 4096 */
MBR_PUTBYTESPERSEC(var->fv_sect, var->fv_sectorsize);
/* 1@13: Sectors per allocation unit: 2**n, n=0..7 */
MBR_PUTSECPERCLUS(var->fv_sect, (1 << fmt->ff_clustshift));
/* 2@14: Reserved sector count: Usually 32 */
MBR_PUTRESVDSECCOUNT(var->fv_sect, fmt->ff_rsvdseccount);
/* 1@16: Number of FAT data structures: always 2 */
MBR_PUTNUMFATS(var->fv_sect, fmt->ff_nfats);
/* 2@17: FAT12/16: Must be 0 for FAT32 */
MBR_PUTROOTENTCNT(var->fv_sect, fmt->ff_rootdirentries);
/* 2@19: FAT12/16: Must be 0, see MBR_TOTSEC32.
* Handled with 4@32: Total count of sectors on the volume
*/
if (fmt->ff_nsectors >= 65536)
{
MBR_PUTTOTSEC32(var->fv_sect, fmt->ff_nsectors);
}
else
{
MBR_PUTTOTSEC16(var->fv_sect, (uint16_t)fmt->ff_nsectors);
}
/* 1@21: Media code: f0, f8, f9-fa, fc-ff */
MBR_PUTMEDIA(var->fv_sect, FAT_DEFAULT_MEDIA_TYPE); /* Only "hard drive" supported */
/* 2@22: FAT12/16: Must be 0, see MBR32_FATSZ32 -- handled in FAT
* specific logic
*/
/* 2@24: Sectors per track geometry value and 2@26: Number of heads
* geometry value
*/
MBR_PUTSECPERTRK(var->fv_sect, FAT_DEFAULT_SECPERTRK);
MBR_PUTNUMHEADS(var->fv_sect, FAT_DEFAULT_NUMHEADS);
/* 4@28: Count of hidden sectors preceding FAT */
MBR_PUTHIDSEC(var->fv_sect, fmt->ff_hidsec);
/* 4@32: Total count of sectors on the volume -- handled above */
/* Most of the rest of the sector depends on the FAT size */
if (fmt->ff_fattype != 32)
{
/* 2@22: FAT12/16: Must be 0, see MBR32_FATSZ32 */
MBR_PUTFATSZ16(var->fv_sect, (uint16_t)var->fv_nfatsects);
/* The following fields are only valid for FAT12/16 */
/* 1@36: Drive number for MSDOS bootstrap -- left zero */
/* 1@37: Reserved (zero) */
/* 1@38: Extended boot signature: 0x29 if following valid */
MBR_PUTBOOTSIG16(var->fv_sect, EXTBOOT_SIGNATURE);
/* 4@39: Volume serial number */
MBR_PUTVOLID16(var->fv_sect, fmt->ff_volumeid);
/* 11@43: Volume label */
memcpy(&var->fv_sect[MBR16_VOLLAB], fmt->ff_volumelabel, 11);
/* 8@54: "FAT12 ", "FAT16 ", or "FAT " */
if (fmt->ff_fattype == 12)
{
memcpy(&var->fv_sect[MBR16_FILESYSTYPE], "FAT12 ", 8);
}
else /* if (fmt->ff_fattype == 16) */
{
memcpy(&var->fv_sect[MBR16_FILESYSTYPE], "FAT16 ", 8);
}
/* Boot code may be placed in the remainder of the sector */
memcpy(&var->fv_sect[MBR16_BOOTCODE], var->fv_bootcodeblob,
var->fv_bootcodesize);
/* Patch in the correct offset to the boot code */
var->fv_sect[MBR16_BOOTCODE + 3] = var->fv_bootcodepatch;
}
else
{
/* The following fields are only valid for FAT32 */
/* 4@36: Count of sectors occupied by one FAT */
MBR_PUTFATSZ32(var->fv_sect, var->fv_nfatsects);
/* 2@40: 0-3:Active FAT, 7=0 both FATS, 7=1 one FAT -- left zero */
/* 2@42: MSB:Major LSB:Minor revision number (0.0) -- left zero */
/* 4@44: Cluster no. of 1st cluster of root dir */
MBR_PUTROOTCLUS(var->fv_sect, FAT32_DEFAULT_ROOT_CLUSTER);
/* 2@48: Sector number of fsinfo structure. Usually 1. */
MBR_PUTFSINFO(var->fv_sect, FAT_DEFAULT_FSINFO_SECTOR);
/* 2@50: Sector number of boot record. Usually 6 */
MBR_PUTBKBOOTSEC(var->fv_sect, fmt->ff_backupboot);
/* 12@52: Reserved (zero) */
/* 1@64: Drive number for MSDOS bootstrap -- left zero */
/* 1@65: Reserved (zero) */
/* 1@66: Extended boot signature: 0x29 if following valid */
MBR_PUTBOOTSIG32(var->fv_sect, EXTBOOT_SIGNATURE);
/* 4@67: Volume serial number */
MBR_PUTVOLID32(var->fv_sect, fmt->ff_volumeid);
/* 11@71: Volume label */
memcpy(&var->fv_sect[MBR32_VOLLAB], fmt->ff_volumelabel, 11);
/* 8@82: "FAT12 ", "FAT16 ", or "FAT " */
memcpy(&var->fv_sect[MBR32_FILESYSTYPE], "FAT32 ", 8);
/* Boot code may be placed in the remainder of the sector */
memcpy(&var->fv_sect[MBR32_BOOTCODE], var->fv_bootcodeblob,
var->fv_bootcodesize);
/* Patch in the correct offset to the boot code */
var->fv_sect[MBR32_BOOTCODE + 3] = var->fv_bootcodepatch;
}
/* The magic bytes at the end of the MBR are common to FAT12/16/32 */
/* 2@510: Valid MBRs have 0x55aa here */
MBR_PUTSIGNATURE(var->fv_sect, BOOT_SIGNATURE16);
}
/****************************************************************************
* Name: mkfatfs_initfsinfo
*
* Description:
* Initialize the FAT32 FSINFO sector image
*
* Input:
* fmt - User specified format parameters
* var - Other format parameters that are not user specifiable
*
* Return:
* None; caller is responsible for providing valid parameters.
*
****************************************************************************/
static inline void mkfatfs_initfsinfo(FAR struct fat_format_s *fmt,
FAR struct fat_var_s *var)
{
2022-08-22 15:17:00 +02:00
UNUSED(fmt);
memset(var->fv_sect, 0, var->fv_sectorsize);
/* 4@0: 0x41615252 = "RRaA" */
FSI_PUTLEADSIG(var->fv_sect, 0x41615252);
/* 480@4: Reserved (zero) */
/* 4@484: 0x61417272 = "rrAa" */
FSI_PUTSTRUCTSIG(var->fv_sect, 0x61417272);
/* 4@488: Last free cluster count on volume */
FSI_PUTFREECOUNT(var->fv_sect, var->fv_nclusters - 1);
/* 4@492: Cluster number of 1st free cluster */
FSI_PUTNXTFREE(var->fv_sect, FAT32_DEFAULT_ROOT_CLUSTER);
/* 12@496: Reserved (zero) */
/* 4@508: 0xaa550000 */
FSI_PUTTRAILSIG(var->fv_sect, BOOT_SIGNATURE32);
}
/****************************************************************************
* Name: mkfatfs_initrootdir
*
* Description:
* Initialize one root directory sector image
*
* Input:
* fmt - User specified format parameters
* var - Other format parameters that are not user specifiable
* sectno - On FAT32, the root directory is a cluster chain.
* This value indicates which sector of the cluster should be
* produced.
*
* Return:
* None; caller is responsible for providing valid parameters.
*
****************************************************************************/
static inline void mkfatfs_initrootdir(FAR struct fat_format_s *fmt,
FAR struct fat_var_s *var, int sectno)
{
memset(var->fv_sect, 0, var->fv_sectorsize);
if (sectno == 0)
{
/* It is only necessary to set data in the first sector of the
* directory
*/
if (memcmp(fmt->ff_volumelabel, " ", 11))
{
memcpy(&var->fv_sect[DIR_NAME], fmt->ff_volumelabel, 11);
}
DIR_PUTATTRIBUTES(var->fv_sect, FATATTR_VOLUMEID);
DIR_PUTCRTIME(var->fv_sect, var->fv_createtime & 0xffff);
DIR_PUTWRTTIME(var->fv_sect, var->fv_createtime & 0xffff);
DIR_PUTCRDATE(var->fv_sect, var->fv_createtime >> 16);
DIR_PUTWRTDATE(var->fv_sect, var->fv_createtime >> 16);
}
}
/****************************************************************************
* Name: mkfatfs_writembr
*
* Description:
* Write the master boot record and, for FAT32, the backup boot record and
* the fsinfo sector.
*
* Input:
* fmt - User specified format parameters
* var - Other format parameters that are not user specifiable
*
* Return:
* Zero on success; negated errno on failure
*
****************************************************************************/
static inline int mkfatfs_writembr(FAR struct fat_format_s *fmt,
FAR struct fat_var_s *var)
{
int sectno;
int ret;
/* Create an image of the configured master boot record */
mkfatfs_initmbr(fmt, var);
/* Write the master boot record as sector zero */
ret = mkfatfs_devwrite(fmt, var, 0);
/* Write all of the reserved sectors */
memset(var->fv_sect, 0, var->fv_sectorsize);
for (sectno = 1; sectno < fmt->ff_rsvdseccount && ret >= 0; sectno++)
{
ret = mkfatfs_devwrite(fmt, var, sectno);
}
/* Write FAT32-specific sectors */
if (ret >= 0 && fmt->ff_fattype == 32)
{
/* Write the backup master boot record */
if (fmt->ff_backupboot != 0)
{
/* Create another copy of the configured master boot record */
mkfatfs_initmbr(fmt, var);
/* Write it to the backup location */
ret = mkfatfs_devwrite(fmt, var, fmt->ff_backupboot);
}
if (ret >= 0)
{
/* Create an image of the fsinfo sector */
mkfatfs_initfsinfo(fmt, var);
/* Write the fsinfo sector */
ret = mkfatfs_devwrite(fmt, var, FAT_DEFAULT_FSINFO_SECTOR);
}
}
return ret;
}
/****************************************************************************
* Name: mkfatfs_writefat
*
* Description:
* Write the FAT sectors.
*
* NOTE: The FAT FS created by this logic is an older style that includes
* the FAT boot record in the master boot record (MBR) and does not support
* a partition table.
*
* Input:
* fmt - User specified format parameters
* var - Other format parameters that are not user specifiable
*
* Return:
* Zero on success; negated errno on failure
*
****************************************************************************/
static inline int mkfatfs_writefat(FAR struct fat_format_s *fmt,
FAR struct fat_var_s *var)
{
off_t offset = fmt->ff_rsvdseccount;
2022-08-22 15:17:00 +02:00
uint8_t fatno;
uint32_t sectno;
int ret;
/* Loop for each FAT copy */
for (fatno = 0; fatno < fmt->ff_nfats; fatno++)
{
/* Loop for each sector in the FAT */
for (sectno = 0; sectno < var->fv_nfatsects; sectno++)
{
memset(var->fv_sect, 0, var->fv_sectorsize);
/* Mark cluster allocations in sector one of each FAT */
if (sectno == 0)
{
memset(var->fv_sect, 0, var->fv_sectorsize);
switch (fmt->ff_fattype)
{
case 12:
/* Mark the first two full FAT entries -- 24 bits,
* 3 bytes total
*/
memset(var->fv_sect, 0xff, 3);
break;
case 16:
/* Mark the first two full FAT entries -- 32 bits,
* 4 bytes total
*/
memset(var->fv_sect, 0xff, 4);
break;
case 32:
default: /* Shouldn't happen */
/* Mark the first two full FAT entries -- 64 bits,
* 8 bytes total
*/
memset(var->fv_sect, 0xff, 8);
/* Cluster 2 is used as the root directory.
* Mark as EOF
*/
var->fv_sect[8] = 0xf8;
memset(&var->fv_sect[9], 0xff, 3);
break;
}
/* Save the media type in the first byte of the FAT */
var->fv_sect[0] = FAT_DEFAULT_MEDIA_TYPE;
}
/* Write the FAT sector */
ret = mkfatfs_devwrite(fmt, var, offset);
if (ret < 0)
{
return ret;
}
offset++;
}
}
return OK;
}
/****************************************************************************
* Name: mkfatfs_writerootdir
*
* Description:
* Write the root directory sectors
*
* Input:
* fmt - User specified format parameters
* var - Other format parameters that are not user specifiable
*
* Return:
* Zero on success; negated errno on failure
*
****************************************************************************/
static inline int mkfatfs_writerootdir(FAR struct fat_format_s *fmt,
FAR struct fat_var_s *var)
{
off_t offset = fmt->ff_rsvdseccount + fmt->ff_nfats * var->fv_nfatsects;
int ret;
int i;
/* Write the root directory after the last FAT. This is the root directory
* area for FAT12/16, and the first cluster on FAT32.
*/
for (i = 0; i < var->fv_nrootdirsects; i++)
{
/* Format the next sector of the root directory */
mkfatfs_initrootdir(fmt, var, i);
/* Write the next sector of the root directory */
ret = mkfatfs_devwrite(fmt, var, offset);
if (ret < 0)
{
return ret;
}
offset++;
}
return 0;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: mkfatfs_writefat
*
* Description:
* Write the configured fat filesystem to the block device
*
* Input:
* fmt - Caller specified format parameters
* var - Other format parameters that are not caller specifiable. (Most
* set by mkfatfs_configfatfs()).
*
* Return:
* Zero on success; negated errno on failure
*
****************************************************************************/
int mkfatfs_writefatfs(FAR struct fat_format_s *fmt,
FAR struct fat_var_s *var)
{
int ret;
/* Write the master boot record (also the backup and fsinfo sectors) */
ret = mkfatfs_writembr(fmt, var);
/* Write FATs */
if (ret >= 0)
{
ret = mkfatfs_writefat(fmt, var);
}
/* Write the root directory after the last FAT. */
if (ret >= 0)
{
ret = mkfatfs_writerootdir(fmt, var);
}
return ret;
}