/**************************************************************************** * 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 #include #include #include #include #include #include #include #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 */ 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" */ strcpy((FAR char *)&var->fv_sect[MBR_OEMNAME], "NUTTX "); /* 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) { 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; 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; }