diff mbox series

[13/17] fs: fat: support mkdir

Message ID 20180720025723.6736-14-takahiro.akashi@linaro.org
State Superseded
Headers show
Series fs: fat: extend FAT write operations | expand

Commit Message

AKASHI Takahiro July 20, 2018, 2:57 a.m. UTC
In this patch, mkdir support is added to FAT file system.
A newly created directory contains only "." and ".." entries.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
 fs/fat/fat_write.c | 138 +++++++++++++++++++++++++++++++++++++++++++++
 fs/fs.c            |   3 +-
 include/fat.h      |   1 +
 3 files changed, 141 insertions(+), 1 deletion(-)

Comments

Heinrich Schuchardt July 20, 2018, 5:14 p.m. UTC | #1
On 07/20/2018 04:57 AM, AKASHI Takahiro wrote:
> In this patch, mkdir support is added to FAT file system.
> A newly created directory contains only "." and ".." entries.
> 
> Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>

The patch does set the creation date of the directory according to the
real time clock but to 1980-01-01T00:00:00Z.

$ ls /mnt/dir1/ -la --full-time
drwxr-xr-x 2 root root  2048 1980-01-01 01:00:00.000000000 +0100 dir3

Please, set the time correctly if the real time clock is available.

Best regards

Heinrich

> ---
>  fs/fat/fat_write.c | 138 +++++++++++++++++++++++++++++++++++++++++++++
>  fs/fs.c            |   3 +-
>  include/fat.h      |   1 +
>  3 files changed, 141 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
> index cc45a33876..781883c9f4 100644
> --- a/fs/fat/fat_write.c
> +++ b/fs/fat/fat_write.c
> @@ -1185,3 +1185,141 @@ int file_fat_write(const char *filename, void *buffer, loff_t offset,
>  {
>  	return file_fat_write_at(filename, offset, buffer, maxsize, actwrite);
>  }
> +
> +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.bs_total_sect;
> +	if (total_sector == 0)
> +		total_sector = (int)cur_part_info.size; /* cast of lbaint_t */
> +
> +	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 archieve 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 = set_cluster(mydata, itr->clust, itr->block,
> +			mydata->clust_size * mydata->sect_size);
> +	if (ret)
> +		printf("Error: writing directory entry\n");
> +
> +exit:
> +	free(dirname_copy);
> +	free(mydata->fatbuf);
> +	free(itr);
> +	free(dotdent);
> +	return ret;
> +}
> diff --git a/fs/fs.c b/fs/fs.c
> index 3cb6b21fe9..a92e060296 100644
> --- a/fs/fs.c
> +++ b/fs/fs.c
> @@ -164,14 +164,15 @@ static struct fstype_info fstypes[] = {
>  		.read = fat_read_file,
>  #ifdef CONFIG_FAT_WRITE
>  		.write = file_fat_write,
> +		.mkdir = fat_mkdir,
>  #else
>  		.write = fs_write_unsupported,
> +		.mkdir = fs_mkdir_unsupported,
>  #endif
>  		.uuid = fs_uuid_unsupported,
>  		.opendir = fat_opendir,
>  		.readdir = fat_readdir,
>  		.closedir = fat_closedir,
> -		.mkdir = fs_mkdir_unsupported,
>  	},
>  #endif
>  #ifdef CONFIG_FS_EXT4
> diff --git a/include/fat.h b/include/fat.h
> index 295da0f243..039b6e03de 100644
> --- a/include/fat.h
> +++ b/include/fat.h
> @@ -237,5 +237,6 @@ int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
>  int fat_opendir(const char *filename, struct fs_dir_stream **dirsp);
>  int fat_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp);
>  void fat_closedir(struct fs_dir_stream *dirs);
> +int fat_mkdir(const char *dirname);
>  void fat_close(void);
>  #endif /* _FAT_H_ */
>
AKASHI Takahiro July 24, 2018, 2:01 a.m. UTC | #2
On Fri, Jul 20, 2018 at 07:14:21PM +0200, Heinrich Schuchardt wrote:
> On 07/20/2018 04:57 AM, AKASHI Takahiro wrote:
> > In this patch, mkdir support is added to FAT file system.
> > A newly created directory contains only "." and ".." entries.
> > 
> > Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> 
> The patch does set the creation date of the directory according to the
> real time clock but to 1980-01-01T00:00:00Z.
> 
> $ ls /mnt/dir1/ -la --full-time
> drwxr-xr-x 2 root root  2048 1980-01-01 01:00:00.000000000 +0100 dir3
> 
> Please, set the time correctly if the real time clock is available.

Good point, but I'd like to put this issue into future TODO list
as it will end up introducing a new API, such as gettimeofday()?

(and this is not a FAT-specific issue.)

Thanks,
-Takahiro AKASHI

> Best regards
> 
> Heinrich
> 
> > ---
> >  fs/fat/fat_write.c | 138 +++++++++++++++++++++++++++++++++++++++++++++
> >  fs/fs.c            |   3 +-
> >  include/fat.h      |   1 +
> >  3 files changed, 141 insertions(+), 1 deletion(-)
> > 
> > diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
> > index cc45a33876..781883c9f4 100644
> > --- a/fs/fat/fat_write.c
> > +++ b/fs/fat/fat_write.c
> > @@ -1185,3 +1185,141 @@ int file_fat_write(const char *filename, void *buffer, loff_t offset,
> >  {
> >  	return file_fat_write_at(filename, offset, buffer, maxsize, actwrite);
> >  }
> > +
> > +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.bs_total_sect;
> > +	if (total_sector == 0)
> > +		total_sector = (int)cur_part_info.size; /* cast of lbaint_t */
> > +
> > +	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 archieve 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 = set_cluster(mydata, itr->clust, itr->block,
> > +			mydata->clust_size * mydata->sect_size);
> > +	if (ret)
> > +		printf("Error: writing directory entry\n");
> > +
> > +exit:
> > +	free(dirname_copy);
> > +	free(mydata->fatbuf);
> > +	free(itr);
> > +	free(dotdent);
> > +	return ret;
> > +}
> > diff --git a/fs/fs.c b/fs/fs.c
> > index 3cb6b21fe9..a92e060296 100644
> > --- a/fs/fs.c
> > +++ b/fs/fs.c
> > @@ -164,14 +164,15 @@ static struct fstype_info fstypes[] = {
> >  		.read = fat_read_file,
> >  #ifdef CONFIG_FAT_WRITE
> >  		.write = file_fat_write,
> > +		.mkdir = fat_mkdir,
> >  #else
> >  		.write = fs_write_unsupported,
> > +		.mkdir = fs_mkdir_unsupported,
> >  #endif
> >  		.uuid = fs_uuid_unsupported,
> >  		.opendir = fat_opendir,
> >  		.readdir = fat_readdir,
> >  		.closedir = fat_closedir,
> > -		.mkdir = fs_mkdir_unsupported,
> >  	},
> >  #endif
> >  #ifdef CONFIG_FS_EXT4
> > diff --git a/include/fat.h b/include/fat.h
> > index 295da0f243..039b6e03de 100644
> > --- a/include/fat.h
> > +++ b/include/fat.h
> > @@ -237,5 +237,6 @@ int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
> >  int fat_opendir(const char *filename, struct fs_dir_stream **dirsp);
> >  int fat_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp);
> >  void fat_closedir(struct fs_dir_stream *dirs);
> > +int fat_mkdir(const char *dirname);
> >  void fat_close(void);
> >  #endif /* _FAT_H_ */
> > 
>
diff mbox series

Patch

diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c
index cc45a33876..781883c9f4 100644
--- a/fs/fat/fat_write.c
+++ b/fs/fat/fat_write.c
@@ -1185,3 +1185,141 @@  int file_fat_write(const char *filename, void *buffer, loff_t offset,
 {
 	return file_fat_write_at(filename, offset, buffer, maxsize, actwrite);
 }
+
+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.bs_total_sect;
+	if (total_sector == 0)
+		total_sector = (int)cur_part_info.size; /* cast of lbaint_t */
+
+	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 archieve 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 = set_cluster(mydata, itr->clust, itr->block,
+			mydata->clust_size * mydata->sect_size);
+	if (ret)
+		printf("Error: writing directory entry\n");
+
+exit:
+	free(dirname_copy);
+	free(mydata->fatbuf);
+	free(itr);
+	free(dotdent);
+	return ret;
+}
diff --git a/fs/fs.c b/fs/fs.c
index 3cb6b21fe9..a92e060296 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -164,14 +164,15 @@  static struct fstype_info fstypes[] = {
 		.read = fat_read_file,
 #ifdef CONFIG_FAT_WRITE
 		.write = file_fat_write,
+		.mkdir = fat_mkdir,
 #else
 		.write = fs_write_unsupported,
+		.mkdir = fs_mkdir_unsupported,
 #endif
 		.uuid = fs_uuid_unsupported,
 		.opendir = fat_opendir,
 		.readdir = fat_readdir,
 		.closedir = fat_closedir,
-		.mkdir = fs_mkdir_unsupported,
 	},
 #endif
 #ifdef CONFIG_FS_EXT4
diff --git a/include/fat.h b/include/fat.h
index 295da0f243..039b6e03de 100644
--- a/include/fat.h
+++ b/include/fat.h
@@ -237,5 +237,6 @@  int fat_read_file(const char *filename, void *buf, loff_t offset, loff_t len,
 int fat_opendir(const char *filename, struct fs_dir_stream **dirsp);
 int fat_readdir(struct fs_dir_stream *dirs, struct fs_dirent **dentp);
 void fat_closedir(struct fs_dir_stream *dirs);
+int fat_mkdir(const char *dirname);
 void fat_close(void);
 #endif /* _FAT_H_ */