#include <command.h>
#include <config.h>
#include <fat.h>
+#include <log.h>
+#include <malloc.h>
#include <asm/byteorder.h>
#include <part.h>
+#include <asm/cache.h>
#include <linux/ctype.h>
#include <div64.h>
#include <linux/math64.h>
return ret;
}
-/*
- * Set short name in directory entry
+/**
+ * set_name() - set short name in directory entry
+ *
+ * @dirent: directory entry
+ * @filename: long file name
*/
static void set_name(dir_entry *dirent, const char *filename)
{
if (len == 0)
return;
- strcpy(s_name, filename);
+ strncpy(s_name, filename, VFAT_MAXLEN_BYTES - 1);
+ s_name[VFAT_MAXLEN_BYTES - 1] = '\0';
uppercase(s_name, len);
period = strchr(s_name, '.');
memcpy(dirent->name, s_name, period_location);
} else {
memcpy(dirent->name, s_name, 6);
+ /*
+ * TODO: Translating two long names with the same first six
+ * characters to the same short name is utterly wrong.
+ * Short names must be unique.
+ */
dirent->name[6] = '~';
dirent->name[7] = '1';
}
return 1;
}
-static int flush_dir_table(fat_itr *itr);
+static int new_dir_table(fat_itr *itr);
+static int flush_dir(fat_itr *itr);
/*
* Fill dir_slot entries with appropriate name, id, and attr
memcpy(itr->dent, slotptr, sizeof(dir_slot));
slotptr--;
counter--;
+
+ if (itr->remaining == 0)
+ flush_dir(itr);
+
+ /* allocate a cluster for more entries */
if (!fat_itr_next(itr))
- if (!itr->dent && !itr->is_root && flush_dir_table(itr))
+ if (!itr->dent &&
+ (!itr->is_root || itr->fsdata->fatsize == 32) &&
+ new_dir_table(itr))
return -1;
}
- if (!itr->dent && !itr->is_root)
- /*
- * don't care return value here because we have already
- * finished completing an entry with name, only ending up
- * no more entry left
- */
- flush_dir_table(itr);
-
return 0;
}
return next_entry;
}
-/*
- * Write at most 'size' bytes from 'buffer' into the specified cluster.
- * Return 0 on success, -1 otherwise.
+/**
+ * set_sectors() - write data to sectors
+ *
+ * Write 'size' bytes from 'buffer' into the specified sector.
+ *
+ * @mydata: data to be written
+ * @startsect: sector to be written to
+ * @buffer: data to be written
+ * @size: bytes to be written (but not more than the size of a cluster)
+ * Return: 0 on success, -1 otherwise
*/
static int
-set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer,
- unsigned long size)
+set_sectors(fsdata *mydata, u32 startsect, u8 *buffer, u32 size)
{
- __u32 idx = 0;
- __u32 startsect;
+ u32 nsects = 0;
int ret;
- if (clustnum > 0)
- startsect = clust_to_sect(mydata, clustnum);
- else
- startsect = mydata->rootdir_sect;
-
- debug("clustnum: %d, startsect: %d\n", clustnum, startsect);
+ debug("startsect: %d\n", startsect);
if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
- printf("FAT: Misaligned buffer address (%p)\n", buffer);
+ debug("FAT: Misaligned buffer address (%p)\n", buffer);
while (size >= mydata->sect_size) {
memcpy(tmpbuf, buffer, mydata->sect_size);
size -= mydata->sect_size;
}
} else if (size >= mydata->sect_size) {
- idx = size / mydata->sect_size;
- ret = disk_write(startsect, idx, buffer);
- if (ret != idx) {
+ nsects = size / mydata->sect_size;
+ ret = disk_write(startsect, nsects, buffer);
+ if (ret != nsects) {
debug("Error writing data (got %d)\n", ret);
return -1;
}
- startsect += idx;
- idx *= mydata->sect_size;
- buffer += idx;
- size -= idx;
+ startsect += nsects;
+ buffer += nsects * mydata->sect_size;
+ size -= nsects * mydata->sect_size;
}
if (size) {
ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
-
+ /* Do not leak content of stack */
+ memset(tmpbuf, 0, mydata->sect_size);
memcpy(tmpbuf, buffer, size);
ret = disk_write(startsect, 1, tmpbuf);
if (ret != 1) {
return 0;
}
+/**
+ * set_cluster() - write data to cluster
+ *
+ * Write 'size' bytes from 'buffer' into the specified cluster.
+ *
+ * @mydata: data to be written
+ * @clustnum: cluster to be written to
+ * @buffer: data to be written
+ * @size: bytes to be written (but not more than the size of a cluster)
+ * Return: 0 on success, -1 otherwise
+ */
+static int
+set_cluster(fsdata *mydata, u32 clustnum, u8 *buffer, u32 size)
+{
+ return set_sectors(mydata, clust_to_sect(mydata, clustnum),
+ buffer, size);
+}
+
+static int
+flush_dir(fat_itr *itr)
+{
+ fsdata *mydata = itr->fsdata;
+ u32 startsect, sect_offset, nsects;
+
+ if (!itr->is_root || mydata->fatsize == 32)
+ return set_cluster(mydata, itr->clust, itr->block,
+ mydata->clust_size * mydata->sect_size);
+
+ sect_offset = itr->clust * mydata->clust_size;
+ startsect = mydata->rootdir_sect + sect_offset;
+ /* do not write past the end of rootdir */
+ nsects = min_t(u32, mydata->clust_size,
+ mydata->rootdir_size - sect_offset);
+
+ return set_sectors(mydata, startsect, itr->block,
+ nsects * mydata->sect_size);
+}
+
+static __u8 tmpbuf_cluster[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN);
+
+/*
+ * Read and modify data on existing and consecutive cluster blocks
+ */
+static int
+get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer,
+ loff_t size, loff_t *gotsize)
+{
+ unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
+ __u32 startsect;
+ loff_t wsize;
+ int clustcount, i, ret;
+
+ *gotsize = 0;
+ if (!size)
+ return 0;
+
+ assert(pos < bytesperclust);
+ startsect = clust_to_sect(mydata, clustnum);
+
+ debug("clustnum: %d, startsect: %d, pos: %lld\n",
+ clustnum, startsect, pos);
+
+ /* partial write at beginning */
+ if (pos) {
+ wsize = min(bytesperclust - pos, size);
+ ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
+ if (ret != mydata->clust_size) {
+ debug("Error reading data (got %d)\n", ret);
+ return -1;
+ }
+
+ memcpy(tmpbuf_cluster + pos, buffer, wsize);
+ ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
+ if (ret != mydata->clust_size) {
+ debug("Error writing data (got %d)\n", ret);
+ return -1;
+ }
+
+ size -= wsize;
+ buffer += wsize;
+ *gotsize += wsize;
+
+ startsect += mydata->clust_size;
+
+ if (!size)
+ return 0;
+ }
+
+ /* full-cluster write */
+ if (size >= bytesperclust) {
+ clustcount = lldiv(size, bytesperclust);
+
+ if (!((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1))) {
+ wsize = clustcount * bytesperclust;
+ ret = disk_write(startsect,
+ clustcount * mydata->clust_size,
+ buffer);
+ if (ret != clustcount * mydata->clust_size) {
+ debug("Error writing data (got %d)\n", ret);
+ return -1;
+ }
+
+ size -= wsize;
+ buffer += wsize;
+ *gotsize += wsize;
+
+ startsect += clustcount * mydata->clust_size;
+ } else {
+ for (i = 0; i < clustcount; i++) {
+ memcpy(tmpbuf_cluster, buffer, bytesperclust);
+ ret = disk_write(startsect,
+ mydata->clust_size,
+ tmpbuf_cluster);
+ if (ret != mydata->clust_size) {
+ debug("Error writing data (got %d)\n",
+ ret);
+ return -1;
+ }
+
+ size -= bytesperclust;
+ buffer += bytesperclust;
+ *gotsize += bytesperclust;
+
+ startsect += mydata->clust_size;
+ }
+ }
+ }
+
+ /* partial write at end */
+ if (size) {
+ wsize = size;
+ ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
+ if (ret != mydata->clust_size) {
+ debug("Error reading data (got %d)\n", ret);
+ return -1;
+ }
+ memcpy(tmpbuf_cluster, buffer, wsize);
+ ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
+ if (ret != mydata->clust_size) {
+ debug("Error writing data (got %d)\n", ret);
+ return -1;
+ }
+
+ size -= wsize;
+ buffer += wsize;
+ *gotsize += wsize;
+ }
+
+ assert(!size);
+
+ return 0;
+}
+
/*
* Find the first empty cluster
*/
}
/*
- * Write directory entries in itr's buffer to block device
+ * Allocate a cluster for additional directory entries
*/
-static int flush_dir_table(fat_itr *itr)
+static int new_dir_table(fat_itr *itr)
{
fsdata *mydata = itr->fsdata;
int dir_newclust = 0;
unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
- if (set_cluster(mydata, itr->clust, itr->block, bytesperclust) != 0) {
- printf("error: writing directory entry\n");
- return -1;
- }
dir_newclust = find_empty_cluster(mydata);
set_fatent_value(mydata, itr->clust, dir_newclust);
if (mydata->fatsize == 32)
set_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, __u8 *buffer,
loff_t maxsize, loff_t *gotsize)
{
- loff_t filesize;
unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
__u32 curclust = START(dentptr);
__u32 endclust = 0, newclust = 0;
- loff_t actsize;
+ u64 cur_pos, filesize;
+ loff_t offset, actsize, wsize;
*gotsize = 0;
- filesize = maxsize;
+ filesize = pos + maxsize;
debug("%llu bytes\n", filesize);
- if (curclust) {
- /*
- * release already-allocated clusters anyway
- */
- if (clear_fatent(mydata, curclust)) {
- printf("Error: clearing FAT entries\n");
+ if (!filesize) {
+ if (!curclust)
+ return 0;
+ if (!CHECK_CLUST(curclust, mydata->fatsize) ||
+ IS_LAST_CLUST(curclust, mydata->fatsize)) {
+ clear_fatent(mydata, curclust);
+ set_start_cluster(mydata, dentptr, 0);
+ return 0;
+ }
+ debug("curclust: 0x%x\n", curclust);
+ debug("Invalid FAT entry\n");
+ return -1;
+ }
+
+ if (!curclust) {
+ assert(pos == 0);
+ goto set_clusters;
+ }
+
+ /* go to cluster at pos */
+ cur_pos = bytesperclust;
+ while (1) {
+ if (pos <= cur_pos)
+ break;
+ if (IS_LAST_CLUST(curclust, mydata->fatsize))
+ break;
+
+ newclust = get_fatent(mydata, curclust);
+ if (!IS_LAST_CLUST(newclust, mydata->fatsize) &&
+ CHECK_CLUST(newclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ debug("Invalid FAT entry\n");
+ return -1;
+ }
+
+ cur_pos += bytesperclust;
+ curclust = newclust;
+ }
+ if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
+ assert(pos == cur_pos);
+ goto set_clusters;
+ }
+
+ assert(pos < cur_pos);
+ cur_pos -= bytesperclust;
+
+ /* overwrite */
+ assert(IS_LAST_CLUST(curclust, mydata->fatsize) ||
+ !CHECK_CLUST(curclust, mydata->fatsize));
+
+ while (1) {
+ /* search for allocated consecutive clusters */
+ actsize = bytesperclust;
+ endclust = curclust;
+ while (1) {
+ if (filesize <= (cur_pos + actsize))
+ break;
+
+ newclust = get_fatent(mydata, endclust);
+
+ if (newclust != endclust + 1)
+ break;
+ if (IS_LAST_CLUST(newclust, mydata->fatsize))
+ break;
+ if (CHECK_CLUST(newclust, mydata->fatsize)) {
+ debug("curclust: 0x%x\n", curclust);
+ debug("Invalid FAT entry\n");
+ return -1;
+ }
+
+ actsize += bytesperclust;
+ endclust = newclust;
+ }
+
+ /* overwrite to <curclust..endclust> */
+ if (pos < cur_pos)
+ offset = 0;
+ else
+ offset = pos - cur_pos;
+ wsize = min_t(unsigned long long, actsize, filesize - cur_pos);
+ wsize -= offset;
+
+ if (get_set_cluster(mydata, curclust, offset,
+ buffer, wsize, &actsize)) {
+ printf("Error get-and-setting cluster\n");
return -1;
}
+ buffer += wsize;
+ *gotsize += wsize;
+ cur_pos += offset + wsize;
+
+ if (filesize <= cur_pos)
+ break;
+
+ if (IS_LAST_CLUST(newclust, mydata->fatsize))
+ /* no more clusters */
+ break;
+
+ curclust = newclust;
}
- curclust = find_empty_cluster(mydata);
- set_start_cluster(mydata, dentptr, curclust);
+ if (filesize <= cur_pos) {
+ /* no more write */
+ newclust = get_fatent(mydata, endclust);
+ if (!IS_LAST_CLUST(newclust, mydata->fatsize)) {
+ /* truncate the rest */
+ clear_fatent(mydata, newclust);
+
+ /* Mark end of file in FAT */
+ if (mydata->fatsize == 12)
+ newclust = 0xfff;
+ else if (mydata->fatsize == 16)
+ newclust = 0xffff;
+ else if (mydata->fatsize == 32)
+ newclust = 0xfffffff;
+ set_fatent_value(mydata, endclust, newclust);
+ }
+
+ return 0;
+ }
+
+ curclust = endclust;
+ filesize -= cur_pos;
+ assert(!do_div(cur_pos, bytesperclust));
+
+set_clusters:
+ /* allocate and write */
+ assert(!pos);
+
+ /* Assure that curclust is valid */
+ if (!curclust) {
+ curclust = find_empty_cluster(mydata);
+ set_start_cluster(mydata, dentptr, curclust);
+ } else {
+ newclust = get_fatent(mydata, curclust);
+
+ if (IS_LAST_CLUST(newclust, mydata->fatsize)) {
+ newclust = determine_fatent(mydata, curclust);
+ set_fatent_value(mydata, curclust, newclust);
+ curclust = newclust;
+ } else {
+ debug("error: something wrong\n");
+ return -1;
+ }
+ }
+ /* TODO: already partially written */
if (check_overflow(mydata, curclust, filesize)) {
printf("Error: no space left: %llu\n", filesize);
return -1;
/* set remaining bytes */
actsize = filesize;
- if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
+ if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) {
debug("error: writing cluster\n");
return -1;
}
return 0;
getit:
- if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
+ if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) {
debug("error: writing cluster\n");
return -1;
}
return itr->dent;
}
- if (!itr->dent && !itr->is_root && flush_dir_table(itr))
+ /* allocate a cluster for more entries */
+ if (!itr->dent &&
+ (!itr->is_root || itr->fsdata->fatsize == 32) &&
+ new_dir_table(itr))
/* indicate that allocating dent failed */
itr->dent = NULL;
return 0;
}
+/**
+ * normalize_longname() - check long file name and convert to lower case
+ *
+ * We assume here that the FAT file system is using an 8bit code page.
+ * Linux typically uses CP437, EDK2 assumes CP1250.
+ *
+ * @l_filename: preallocated buffer receiving the normalized name
+ * @filename: filename to normalize
+ * Return: 0 on success, -1 on failure
+ */
static int normalize_longname(char *l_filename, const char *filename)
{
- const char *p, legal[] = "!#$%&\'()-.@^`_{}~";
- char c;
- int name_len;
-
- /* Check that the filename is valid */
- for (p = filename; p < filename + strlen(filename); p++) {
- c = *p;
-
- if (('0' <= c) && (c <= '9'))
- continue;
- if (('A' <= c) && (c <= 'Z'))
- continue;
- if (('a' <= c) && (c <= 'z'))
- continue;
- if (strchr(legal, c))
- continue;
- /* extended code */
- if ((0x80 <= c) && (c <= 0xff))
- continue;
+ const char *p, illegal[] = "<>:\"/\\|?*";
+ if (strlen(filename) >= VFAT_MAXLEN_BYTES)
return -1;
- }
- /* Normalize it */
- name_len = strlen(filename);
- if (name_len >= VFAT_MAXLEN_BYTES)
- /* should return an error? */
- name_len = VFAT_MAXLEN_BYTES - 1;
+ for (p = filename; *p; ++p) {
+ if ((unsigned char)*p < 0x20)
+ return -1;
+ if (strchr(illegal, *p))
+ return -1;
+ }
- memcpy(l_filename, filename, name_len);
- l_filename[name_len] = 0; /* terminate the string */
- downcase(l_filename, INT_MAX);
+ strcpy(l_filename, filename);
+ downcase(l_filename, VFAT_MAXLEN_BYTES);
return 0;
}
goto exit;
}
+ /* A file exists */
+ if (pos == -1)
+ /* Append to the end */
+ pos = FAT2CPU32(retdent->size);
+ if (pos > retdent->size) {
+ /* No hole allowed */
+ ret = -EINVAL;
+ goto exit;
+ }
+
/* Update file size in a directory entry */
retdent->size = cpu_to_le32(pos + size);
} else {
goto exit;
}
+ if (pos) {
+ /* No hole allowed */
+ ret = -EINVAL;
+ goto exit;
+ }
+
memset(itr->dent, 0, sizeof(*itr->dent));
- /* Set short name to set alias checksum field in dir_slot */
+ /* Calculate checksum for short name */
set_name(itr->dent, filename);
+
+ /* Set long name entries */
if (fill_dir_slot(itr, filename)) {
ret = -EIO;
goto exit;
}
- /* Set attribute as archive for regular file */
+ /* Set short name entry */
fill_dentry(itr->fsdata, itr->dent, filename, 0, size, 0x20);
retdent = itr->dent;
}
/* Write directory table to device */
- ret = set_cluster(mydata, itr->clust, itr->block,
- mydata->clust_size * mydata->sect_size);
+ ret = flush_dir(itr);
if (ret) {
printf("Error: writing directory entry\n");
ret = -EIO;
int file_fat_write(const char *filename, void *buffer, loff_t offset,
loff_t maxsize, loff_t *actwrite)
{
- if (offset != 0) {
- printf("Error: non zero offset is currently not supported.\n");
- return -EINVAL;
+ return file_fat_write_at(filename, offset, buffer, maxsize, actwrite);
+}
+
+static int fat_dir_entries(fat_itr *itr)
+{
+ fat_itr *dirs;
+ fsdata fsdata = { .fatbuf = NULL, }, *mydata = &fsdata;
+ /* for FATBUFSIZE */
+ int count;
+
+ dirs = malloc_cache_aligned(sizeof(fat_itr));
+ if (!dirs) {
+ debug("Error: allocating memory\n");
+ count = -ENOMEM;
+ goto exit;
}
- return file_fat_write_at(filename, offset, buffer, maxsize, actwrite);
+ /* duplicate fsdata */
+ fat_itr_child(dirs, itr);
+ fsdata = *dirs->fsdata;
+
+ /* allocate local fat buffer */
+ fsdata.fatbuf = malloc_cache_aligned(FATBUFSIZE);
+ if (!fsdata.fatbuf) {
+ debug("Error: allocating memory\n");
+ count = -ENOMEM;
+ goto exit;
+ }
+ fsdata.fatbufnum = -1;
+ dirs->fsdata = &fsdata;
+
+ for (count = 0; fat_itr_next(dirs); count++)
+ ;
+
+exit:
+ free(fsdata.fatbuf);
+ free(dirs);
+ return count;
+}
+
+static int delete_dentry(fat_itr *itr)
+{
+ fsdata *mydata = itr->fsdata;
+ dir_entry *dentptr = itr->dent;
+
+ /* free cluster blocks */
+ clear_fatent(mydata, START(dentptr));
+ if (flush_dirty_fat_buffer(mydata) < 0) {
+ printf("Error: flush fat buffer\n");
+ return -EIO;
+ }
+
+ /*
+ * update a directory entry
+ * TODO:
+ * - long file name support
+ * - find and mark the "new" first invalid entry as name[0]=0x00
+ */
+ memset(dentptr, 0, sizeof(*dentptr));
+ dentptr->name[0] = 0xe5;
+
+ if (flush_dir(itr)) {
+ printf("error: writing directory entry\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int fat_unlink(const char *filename)
+{
+ fsdata fsdata = { .fatbuf = NULL, };
+ fat_itr *itr = NULL;
+ int n_entries, ret;
+ char *filename_copy, *dirname, *basename;
+
+ filename_copy = strdup(filename);
+ if (!filename_copy) {
+ printf("Error: allocating memory\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+ split_filename(filename_copy, &dirname, &basename);
+
+ if (!strcmp(dirname, "/") && !strcmp(basename, "")) {
+ printf("Error: cannot remove root\n");
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ itr = malloc_cache_aligned(sizeof(fat_itr));
+ if (!itr) {
+ printf("Error: allocating memory\n");
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ ret = fat_itr_root(itr, &fsdata);
+ if (ret)
+ goto exit;
+
+ total_sector = fsdata.total_sect;
+
+ ret = fat_itr_resolve(itr, dirname, TYPE_DIR);
+ if (ret) {
+ printf("%s: doesn't exist (%d)\n", dirname, ret);
+ ret = -ENOENT;
+ goto exit;
+ }
+
+ if (!find_directory_entry(itr, basename)) {
+ printf("%s: doesn't exist\n", basename);
+ ret = -ENOENT;
+ goto exit;
+ }
+
+ if (fat_itr_isdir(itr)) {
+ n_entries = fat_dir_entries(itr);
+ if (n_entries < 0) {
+ ret = n_entries;
+ goto exit;
+ }
+ if (n_entries > 2) {
+ printf("Error: directory is not empty: %d\n",
+ n_entries);
+ ret = -EINVAL;
+ goto exit;
+ }
+ }
+
+ ret = delete_dentry(itr);
+
+exit:
+ free(fsdata.fatbuf);
+ free(itr);
+ free(filename_copy);
+
+ return ret;
+}
+
+int fat_mkdir(const char *new_dirname)
+{
+ dir_entry *retdent;
+ fsdata datablock = { .fatbuf = NULL, };
+ fsdata *mydata = &datablock;
+ fat_itr *itr = NULL;
+ char *dirname_copy, *parent, *dirname;
+ char l_dirname[VFAT_MAXLEN_BYTES];
+ int ret = -1;
+ loff_t actwrite;
+ unsigned int bytesperclust;
+ dir_entry *dotdent = NULL;
+
+ dirname_copy = strdup(new_dirname);
+ if (!dirname_copy)
+ goto exit;
+
+ split_filename(dirname_copy, &parent, &dirname);
+ if (!strlen(dirname)) {
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ if (normalize_longname(l_dirname, dirname)) {
+ printf("FAT: illegal filename (%s)\n", dirname);
+ ret = -EINVAL;
+ goto exit;
+ }
+
+ itr = malloc_cache_aligned(sizeof(fat_itr));
+ if (!itr) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ ret = fat_itr_root(itr, &datablock);
+ if (ret)
+ goto exit;
+
+ total_sector = datablock.total_sect;
+
+ ret = fat_itr_resolve(itr, parent, TYPE_DIR);
+ if (ret) {
+ printf("%s: doesn't exist (%d)\n", parent, ret);
+ goto exit;
+ }
+
+ retdent = find_directory_entry(itr, l_dirname);
+
+ if (retdent) {
+ printf("%s: already exists\n", l_dirname);
+ ret = -EEXIST;
+ goto exit;
+ } else {
+ if (itr->is_root) {
+ /* root dir cannot have "." or ".." */
+ if (!strcmp(l_dirname, ".") ||
+ !strcmp(l_dirname, "..")) {
+ ret = -EINVAL;
+ goto exit;
+ }
+ }
+
+ if (!itr->dent) {
+ printf("Error: allocating new dir entry\n");
+ ret = -EIO;
+ goto exit;
+ }
+
+ memset(itr->dent, 0, sizeof(*itr->dent));
+
+ /* Set short name to set alias checksum field in dir_slot */
+ set_name(itr->dent, dirname);
+ fill_dir_slot(itr, dirname);
+
+ /* Set attribute as archive for regular file */
+ fill_dentry(itr->fsdata, itr->dent, dirname, 0, 0,
+ ATTR_DIR | ATTR_ARCH);
+
+ retdent = itr->dent;
+ }
+
+ /* Default entries */
+ bytesperclust = mydata->clust_size * mydata->sect_size;
+ dotdent = malloc_cache_aligned(bytesperclust);
+ if (!dotdent) {
+ ret = -ENOMEM;
+ goto exit;
+ }
+ memset(dotdent, 0, bytesperclust);
+
+ memcpy(dotdent[0].name, ". ", 8);
+ memcpy(dotdent[0].ext, " ", 3);
+ dotdent[0].attr = ATTR_DIR | ATTR_ARCH;
+
+ memcpy(dotdent[1].name, ".. ", 8);
+ memcpy(dotdent[1].ext, " ", 3);
+ dotdent[1].attr = ATTR_DIR | ATTR_ARCH;
+ set_start_cluster(mydata, &dotdent[1], itr->start_clust);
+
+ ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
+ bytesperclust, &actwrite);
+ if (ret < 0) {
+ printf("Error: writing contents\n");
+ goto exit;
+ }
+ /* Write twice for "." */
+ set_start_cluster(mydata, &dotdent[0], START(retdent));
+ ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
+ bytesperclust, &actwrite);
+ if (ret < 0) {
+ printf("Error: writing contents\n");
+ goto exit;
+ }
+
+ /* Flush fat buffer */
+ ret = flush_dirty_fat_buffer(mydata);
+ if (ret) {
+ printf("Error: flush fat buffer\n");
+ goto exit;
+ }
+
+ /* Write directory table to device */
+ ret = flush_dir(itr);
+ if (ret)
+ printf("Error: writing directory entry\n");
+
+exit:
+ free(dirname_copy);
+ free(mydata->fatbuf);
+ free(itr);
+ free(dotdent);
+ return ret;
}