Many file systems use a copy&paste implementation of dirent to on-disk file type conversions. Create a common implementation to be used by file systems with some useful conversion helpers to reduce open coded file type conversions in file system code. Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- include/linux/file_type.h | 107 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fs.h | 17 +------- 2 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 include/linux/file_type.h Fixed a comment by Darrick and a build warning reported by kbuild robot. These changes are local to the common implementation, so not re-spamming with all the non changed fs patches. v2: - s/DT_MASK/S_DT_MASK to fix redefinition in drivers/scsi/qla2xxx/qla_def.h - explicit initialization of fs_dtype_by_ftype[] using [FT_*] = DT_* v1: - initial implementation diff --git a/include/linux/file_type.h b/include/linux/file_type.h new file mode 100644 index 0000000..bb218f4 --- /dev/null +++ b/include/linux/file_type.h @@ -0,0 +1,107 @@ +#ifndef _LINUX_FILE_TYPE_H +#define _LINUX_FILE_TYPE_H + +/* + * This is a common implementation of dirent to fs on-disk + * file type conversion. Although the fs on-disk bits are + * specific to every file system, in practice, many file systems + * use the exact same on-disk format to describe the lower 3 + * file type bits that represent the 7 POSIX file types. + * All those file systems can use this generic code for the + * conversions: + * i_mode -> fs on-disk file type (ftype) + * fs on-disk file type (ftype) -> dirent file type (dtype) + * i_mode -> dirent file type (dtype) + */ + +/* + * struct dirent file types + * exposed to user via getdents(2), readdir(3) + * + * These match bits 12..15 of stat.st_mode + * (ie "(i_mode >> 12) & 15"). + */ +#define S_DT_SHIFT 12 +#define S_DT(mode) (((mode) & S_IFMT) >> S_DT_SHIFT) +#define S_DT_MASK (S_IFMT >> S_DT_SHIFT) + +#define DT_UNKNOWN 0 +#define DT_FIFO S_DT(S_IFIFO) /* 1 */ +#define DT_CHR S_DT(S_IFCHR) /* 2 */ +#define DT_DIR S_DT(S_IFDIR) /* 4 */ +#define DT_BLK S_DT(S_IFBLK) /* 6 */ +#define DT_REG S_DT(S_IFREG) /* 8 */ +#define DT_LNK S_DT(S_IFLNK) /* 10 */ +#define DT_SOCK S_DT(S_IFSOCK) /* 12 */ +#define DT_WHT 14 + +#define DT_MAX (S_DT_MASK + 1) /* 16 */ + +/* + * fs on-disk file types. + * Only the low 3 bits are used for the POSIX file types. + * Other bits are reserved for fs private use. + * + * Note that no fs currently stores the whiteout type on-disk, + * so whiteout dirents are exposed to user as DT_CHR. + */ +#define FT_UNKNOWN 0 +#define FT_REG_FILE 1 +#define FT_DIR 2 +#define FT_CHRDEV 3 +#define FT_BLKDEV 4 +#define FT_FIFO 5 +#define FT_SOCK 6 +#define FT_SYMLINK 7 + +#define FT_MAX 8 + +/* + * fs on-disk file type to dirent file type conversion + */ +static unsigned char fs_dtype_by_ftype[FT_MAX] = { + [FT_UNKNOWN] = DT_UNKNOWN, + [FT_REG_FILE] = DT_REG, + [FT_DIR] = DT_DIR, + [FT_CHRDEV] = DT_CHR, + [FT_BLKDEV] = DT_BLK, + [FT_FIFO] = DT_FIFO, + [FT_SOCK] = DT_SOCK, + [FT_SYMLINK] = DT_LNK +}; + +static inline unsigned char fs_dtype(int filetype) +{ + if (filetype >= FT_MAX) + return DT_UNKNOWN; + + return fs_dtype_by_ftype[filetype]; +} + +/* + * dirent file type to fs on-disk file type conversion + * Values not initialized explicitly are FT_UNKNOWN (0). + */ +static unsigned char fs_ftype_by_dtype[DT_MAX] = { + [DT_REG] = FT_REG_FILE, + [DT_DIR] = FT_DIR, + [DT_LNK] = FT_SYMLINK, + [DT_CHR] = FT_CHRDEV, + [DT_BLK] = FT_BLKDEV, + [DT_FIFO] = FT_FIFO, + [DT_SOCK] = FT_SOCK, +}; + +/* st_mode to fs on-disk file type conversion */ +static inline unsigned char fs_umode_to_ftype(umode_t mode) +{ + return fs_ftype_by_dtype[S_DT(mode)]; +} + +/* st_mode to dirent file type conversion */ +static inline unsigned char fs_umode_to_dtype(umode_t mode) +{ + return fs_dtype(fs_umode_to_ftype(mode)); +} + +#endif diff --git a/include/linux/fs.h b/include/linux/fs.h index e6e4146..8f1580d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -31,6 +31,7 @@ #include <linux/workqueue.h> #include <linux/percpu-rwsem.h> #include <linux/delayed_call.h> +#include <linux/file_type.h> #include <asm/byteorder.h> #include <uapi/linux/fs.h> @@ -1582,22 +1583,6 @@ int fiemap_fill_next_extent(struct fiemap_extent_info *info, u64 logical, int fiemap_check_flags(struct fiemap_extent_info *fieinfo, u32 fs_flags); /* - * File types - * - * NOTE! These match bits 12..15 of stat.st_mode - * (ie "(i_mode >> 12) & 15"). - */ -#define DT_UNKNOWN 0 -#define DT_FIFO 1 -#define DT_CHR 2 -#define DT_DIR 4 -#define DT_BLK 6 -#define DT_REG 8 -#define DT_LNK 10 -#define DT_SOCK 12 -#define DT_WHT 14 - -/* * This is the "filldir" function type, used by readdir() to let * the kernel specify what kind of dirent layout it wants to have. * This allows the kernel to read directories into kernel space or -- 2.7.4 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html