From patchwork Tue Nov 19 09:37:06 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11251645 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 712446C1 for ; Tue, 19 Nov 2019 09:41:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 2B56D22304 for ; Tue, 19 Nov 2019 09:41:31 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="NgKd2Rrs" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726860AbfKSJla (ORCPT ); Tue, 19 Nov 2019 04:41:30 -0500 Received: from mailout2.samsung.com ([203.254.224.25]:40629 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727336AbfKSJkZ (ORCPT ); Tue, 19 Nov 2019 04:40:25 -0500 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20191119094022epoutp027b77accfbf3575155235e02cd6165ff5~Yhu0PTm0C0315703157epoutp02x for ; Tue, 19 Nov 2019 09:40:22 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20191119094022epoutp027b77accfbf3575155235e02cd6165ff5~Yhu0PTm0C0315703157epoutp02x DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574156422; bh=7WWktjOwzzFi4tqacBsRXJRcgnviLaJJNoXjzq6Hy38=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NgKd2RrsMRiVb6LzD+foNgi+oZROs+ZGDuPX+smt2B190YJORskdq3fp2Mqe7t8kU 0qh7LpaYXMagQUncAxQI5lxiQLrEp77NM5YETz7m53HAtWW0CktaD2J2k9m1KnV8mt 9gA8b060AvHxbT8uwt2NMPy9mQakxBwTRnvSqDOs= Received: from epsnrtp3.localdomain (unknown [182.195.42.164]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20191119094021epcas1p13605b181bf2fca6970d4d04ca6e6377c~Yhuzp23R40953509535epcas1p1k; Tue, 19 Nov 2019 09:40:21 +0000 (GMT) Received: from epsmges1p2.samsung.com (unknown [182.195.40.166]) by epsnrtp3.localdomain (Postfix) with ESMTP id 47HLQr5Nc9zMqYkc; Tue, 19 Nov 2019 09:40:20 +0000 (GMT) Received: from epcas1p2.samsung.com ( [182.195.41.46]) by epsmges1p2.samsung.com (Symantec Messaging Gateway) with SMTP id C7.73.04235.488B3DD5; Tue, 19 Nov 2019 18:40:20 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p2.samsung.com (KnoxPortal) with ESMTPA id 20191119094020epcas1p26de9d7e4e2ab8ad5d6ecaf23e2dfdca8~YhuyPs_Uo0990309903epcas1p2F; Tue, 19 Nov 2019 09:40:20 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119094020epsmtrp12a2ff50a31dd701a126c44f153c02b08~YhuyO7gUE0081500815epsmtrp1Y; Tue, 19 Nov 2019 09:40:20 +0000 (GMT) X-AuditID: b6c32a36-e07ff7000000108b-f3-5dd3b884225a Received: from epsmtip2.samsung.com ( [182.195.34.31]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id AB.92.03814.488B3DD5; Tue, 19 Nov 2019 18:40:20 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20191119094020epsmtip2e9ecd8a7b993a01642930fc6cb11549f~YhuyEqb1i0597405974epsmtip2b; Tue, 19 Nov 2019 09:40:20 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, Namjae Jeon Subject: [PATCH v3 01/13] exfat: add in-memory and on-disk structures and headers Date: Tue, 19 Nov 2019 04:37:06 -0500 Message-Id: <20191119093718.3501-2-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119093718.3501-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFmpjk+LIzCtJLcpLzFFi42LZdlhTT7dlx+VYgxsNmhaHH09it2hevJ7N YuXqo0wW1+/eYrbYs/cki8XlXXPYLP7Pes5q8WN6vcWWf0dYLS69/8DiwOWxc9Zddo/9c9ew e+y+2cDm0bdlFaPH5tPVHp83yXkc2v6GzeP2s20sARxROTYZqYkpqUUKqXnJ+SmZeem2St7B 8c7xpmYGhrqGlhbmSgp5ibmptkouPgG6bpk5QCcqKZQl5pQChQISi4uV9O1sivJLS1IVMvKL S2yVUgtScgoMDQr0ihNzi0vz0vWS83OtDA0MjEyBKhNyMvY9X8dWsPkAY8WCfy3MDYwn5zN2 MXJwSAiYSPR383cxcnEICexglLi8awsLhPOJUeLphZmMEM43RomDGx4COZxgHX1HvkIl9jJK HO67yQbX8mPzPTaQuWwC2hJ/toiCNIgI2Etsnn0AbCyzwBFGiRdfJrCDJIQFgiUmXrvIDGKz CKhKXF10hhWkl1fAWmLKIxGIZfISqzccACvhFLCReDh7GdhiCYEzbBKn522EushF4sqLxWwQ trDEq+Nb2CFsKYmX/W3sEH9WS3zczwwR7gC64bsthG0scXP9BrC1zAKaEut36UOEFSV2/p4L Np1ZgE/i3dceVogpvBIdbUIQJaoSfZcOM0HY0hJd7R+glnpIdN2dBw2efkaJrqZXjBMY5WYh bFjAyLiKUSy1oDg3PbXYsMAIOcY2MYLToJbZDsZF53wOMQpwMCrx8J5QuRwrxJpYVlyZe4hR goNZSYTX79GFWCHelMTKqtSi/Pii0pzU4kOMpsBwnMgsJZqcD0zReSXxhqZGxsbGFiZm5mam xkrivBw/LsYKCaQnlqRmp6YWpBbB9DFxcEo1MAYaXpJb1XNzud7RO9tTX/pdW7ki92D6LbVJ Zy551JR+/9xos+jzpR1fmydmtd5R6l1ovHP7sapLVguz/0a/dZI3l5hU4+ka/mHamh2/peJE xXbNimQ5EGB3VkWgMzo2fdZ5YdN/jocPyf/+k9a+YvtRPzklm8CueXwX36om6F956r/SvK3j 4EolluKMREMt5qLiRABE4EsrmQMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrHLMWRmVeSWpSXmKPExsWy7bCSvG7LjsuxBo8+ClgcfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9RZb/h1htbj0/gOLA5fHzll32T32z13D 7rH7ZgObR9+WVYwem09Xe3zeJOdxaPsbNo/bz7axBHBEcdmkpOZklqUW6dslcGXse76OrWDz AcaKBf9amBsYT85n7GLk5JAQMJHoO/IVzBYS2M0osW6uLERcWuLYiTPMXYwcQLawxOHDxV2M XEAlHxglbrxczg4SZxPQlvizRRSkXETAUaJ312EWkBpmgXOMEjufLWMEqREWCJTYuSYfpIZF QFXi6qIzrCBhXgFriSmPRCA2yUus3nCAGcTmFLCReDh7GdQ11hKNj5rZJzDyLWBkWMUomVpQ nJueW2xYYJSXWq5XnJhbXJqXrpecn7uJERyqWlo7GE+ciD/EKMDBqMTDe0LlcqwQa2JZcWXu IUYJDmYlEV6/RxdihXhTEiurUovy44tKc1KLDzFKc7AoifPK5x+LFBJITyxJzU5NLUgtgsky cXBKNTAGr7h75/AmC/bX+oYMJ2vDCpUrUxaVxeRk6C2J+dWYp3BXvjWy/dal9dbhK9M/vHR9 vmNnnJQFp9XLh2c39nNc7FFs6Jsl/Pfp0tglX7WWr81k9zTKuL0y9bWUFSOP0DoOPpM31bdr q9Q3613WOdmkFrb+14QlCSHHri39ZNn0R//tAxZG8TYlluKMREMt5qLiRAAirkpQUQIAAA== X-CMS-MailID: 20191119094020epcas1p26de9d7e4e2ab8ad5d6ecaf23e2dfdca8 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119094020epcas1p26de9d7e4e2ab8ad5d6ecaf23e2dfdca8 References: <20191119093718.3501-1-namjae.jeon@samsung.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This adds in-memory and on-disk structures and headers. Signed-off-by: Namjae Jeon Signed-off-by: Sungjong Seo --- fs/exfat/exfat_fs.h | 534 +++++++++++++++++++++++++++++++++++++++++++ fs/exfat/exfat_raw.h | 190 +++++++++++++++ 2 files changed, 724 insertions(+) create mode 100644 fs/exfat/exfat_fs.h create mode 100644 fs/exfat/exfat_raw.h diff --git a/fs/exfat/exfat_fs.h b/fs/exfat/exfat_fs.h new file mode 100644 index 000000000000..6e1c738e520a --- /dev/null +++ b/fs/exfat/exfat_fs.h @@ -0,0 +1,534 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#ifndef _EXFAT_H +#define _EXFAT_H + +#include +#include + +#define EXFAT_SUPER_MAGIC (0x2011BAB0UL) +#define EXFAT_ROOT_INO 1 + +enum exfat_time_mode { + TM_CREATE, + TM_MODIFY, + TM_ACCESS, +}; + +/* + * exfat error flags + */ +enum exfat_error_mode { + EXFAT_ERRORS_CONT, /* ignore error and continue */ + EXFAT_ERRORS_PANIC, /* panic on error */ + EXFAT_ERRORS_RO, /* remount r/o on error */ +}; + +/* + * exfat nls lossy flag + */ +#define NLS_NAME_NO_LOSSY (0x00) /* no lossy */ +#define NLS_NAME_LOSSY (0x01) /* just detected incorrect filename(s) */ +#define NLS_NAME_OVERLEN (0x02) /* the length is over than its limit */ + +/* + * exfat common MACRO + */ +#define CLUSTER_32(x) ((unsigned int)((x) & 0xFFFFFFFFU)) +#define EOF_CLUSTER CLUSTER_32(~0) +#define BAD_CLUSTER (0xFFFFFFF7U) +#define FREE_CLUSTER (0) +#define BASE_CLUSTER (2) + +#define EXFAT_HASH_BITS 8 +#define EXFAT_HASH_SIZE (1UL << EXFAT_HASH_BITS) + +/* + * Type Definitions + */ +#define ES_2_ENTRIES 2 +#define ES_ALL_ENTRIES 0 + +#define DIR_DELETED 0xFFFF0321 + +/* type values */ +#define TYPE_UNUSED 0x0000 +#define TYPE_DELETED 0x0001 +#define TYPE_INVALID 0x0002 +#define TYPE_CRITICAL_PRI 0x0100 +#define TYPE_BITMAP 0x0101 +#define TYPE_UPCASE 0x0102 +#define TYPE_VOLUME 0x0103 +#define TYPE_DIR 0x0104 +#define TYPE_FILE 0x011F +#define TYPE_CRITICAL_SEC 0x0200 +#define TYPE_STREAM 0x0201 +#define TYPE_EXTEND 0x0202 +#define TYPE_ACL 0x0203 +#define TYPE_BENIGN_PRI 0x0400 +#define TYPE_GUID 0x0401 +#define TYPE_PADDING 0x0402 +#define TYPE_ACLTAB 0x0403 +#define TYPE_BENIGN_SEC 0x0800 +#define TYPE_ALL 0x0FFF + +#define MAX_CHARSET_SIZE 6 /* max size of multi-byte character */ +#define MAX_NAME_LENGTH 255 /* max len of file name excluding NULL */ +#define MAX_VFSNAME_BUF_SIZE ((MAX_NAME_LENGTH + 1) * MAX_CHARSET_SIZE) + +#define FAT_CACHE_SIZE 128 +#define FAT_CACHE_HASH_SIZE 64 +#define BUF_CACHE_SIZE 256 +#define BUF_CACHE_HASH_SIZE 64 + +#define EXFAT_HINT_NONE -1 +#define EXFAT_MIN_SUBDIR 2 + +/* + * helpers for cluster size to byte conversion. + */ +#define EXFAT_CLU_TO_B(b, sbi) ((b) << (sbi)->cluster_size_bits) +#define EXFAT_B_TO_CLU(b, sbi) ((b) >> (sbi)->cluster_size_bits) +#define EXFAT_B_TO_CLU_ROUND_UP(b, sbi) \ + (((b - 1) >> (sbi)->cluster_size_bits) + 1) +#define EXFAT_CLU_OFFSET(off, sbi) ((off) & ((sbi)->cluster_size - 1)) + +/* + * helpers for block size to byte conversion. + */ +#define EXFAT_BLK_TO_B(b, sb) ((b) << (sb)->s_blocksize_bits) +#define EXFAT_B_TO_BLK(b, sb) ((b) >> (sb)->s_blocksize_bits) +#define EXFAT_B_TO_BLK_ROUND_UP(b, sb) \ + (((b - 1) >> (sb)->s_blocksize_bits) + 1) +#define EXFAT_BLK_OFFSET(off, sb) ((off) & ((sb)->s_blocksize - 1)) + +/* + * helpers for block size to dentry size conversion. + */ +#define EXFAT_B_TO_DEN_IDX(b, sbi) \ + ((b) << ((sbi)->cluster_size_bits - DENTRY_SIZE_BITS)) +#define EXFAT_B_TO_DEN(b) ((b) >> DENTRY_SIZE_BITS) +#define EXFAT_DEN_TO_B(b) ((b) << DENTRY_SIZE_BITS) + +struct exfat_timestamp { + unsigned short sec; /* 0 ~ 59 */ + unsigned short min; /* 0 ~ 59 */ + unsigned short hour; /* 0 ~ 23 */ + unsigned short day; /* 1 ~ 31 */ + unsigned short mon; /* 1 ~ 12 */ + unsigned short year; /* 0 ~ 127 (since 1980) */ +}; + +struct exfat_date_time { + unsigned short year; + unsigned short month; + unsigned short day; + unsigned short hour; + unsigned short minute; + unsigned short second; + unsigned short milli_second; +}; + +struct exfat_dentry_namebuf { + char *lfn; + int lfnbuf_len; /* usally MAX_UNINAME_BUF_SIZE */ +}; + +/* unicode name structure */ +struct exfat_uni_name { + /* +3 for null and for converting */ + unsigned short name[MAX_NAME_LENGTH + 3]; + unsigned short name_hash; + unsigned char name_len; +}; + +/* directory structure */ +struct exfat_chain { + unsigned int dir; + unsigned int size; + unsigned char flags; +}; + +/* first empty entry hint information */ +struct exfat_hint_femp { + /* entry index of a directory */ + int eidx; + /* count of continuous empty entry */ + int count; + /* the cluster that first empty slot exists in */ + struct exfat_chain cur; +}; + +/* hint structure */ +struct exfat_hint { + unsigned int clu; + union { + unsigned int off; /* cluster offset */ + int eidx; /* entry index */ + }; +}; + +struct exfat_entry_set_cache { + /* sector number that contains file_entry */ + sector_t sector; + /* byte offset in the sector */ + unsigned int offset; + /* flag in stream entry. 01 for cluster chain, 03 for contig. */ + int alloc_flag; + unsigned int num_entries; + struct exfat_dentry entries[]; +}; + +struct exfat_dir_entry { + struct exfat_chain dir; + int entry; + unsigned int type; + unsigned int start_clu; + unsigned char flags; + unsigned short attr; + loff_t size; + unsigned int num_subdirs; + struct exfat_date_time create_timestamp; + struct exfat_date_time modify_timestamp; + struct exfat_date_time access_timestamp; + struct exfat_dentry_namebuf namebuf; +}; + +/* + * exfat mount in-memory data + */ +struct exfat_mount_options { + kuid_t fs_uid; + kgid_t fs_gid; + unsigned short fs_fmask; + unsigned short fs_dmask; + /* permission for setting the [am]time */ + unsigned short allow_utime; + /* charset for filename input/display */ + char *iocharset; + unsigned char utf8; + unsigned char case_sensitive; + unsigned char tz_utc; + /* on error: continue, panic, remount-ro */ + enum exfat_error_mode errors; + /* flag on if -o dicard specified and device support discard() */ + unsigned char discard; +}; + +/* + * EXFAT file system superblock in-memory data + */ +struct exfat_sb_info { + unsigned int vol_type; /* volume FAT type */ + unsigned int vol_id; /* volume serial number */ + unsigned long long num_sectors; /* num of sectors in volume */ + unsigned int num_clusters; /* num of clusters in volume */ + unsigned int cluster_size; /* cluster size in bytes */ + unsigned int cluster_size_bits; + unsigned int sect_per_clus; /* cluster size in sectors */ + unsigned int sect_per_clus_bits; + unsigned long long FAT1_start_sector; /* FAT1 start sector */ + unsigned long long FAT2_start_sector; /* FAT2 start sector */ + unsigned long long root_start_sector; /* root dir start sector */ + unsigned long long data_start_sector; /* data area start sector */ + unsigned int num_FAT_sectors; /* num of FAT sectors */ + unsigned int root_dir; /* root dir cluster */ + unsigned int dentries_in_root; /* num of dentries in root dir */ + unsigned int dentries_per_clu; /* num of dentries per cluster */ + unsigned int vol_flag; /* volume dirty flag */ + struct buffer_head *pbr_bh; /* buffer_head of PBR sector */ + + unsigned int map_clu; /* allocation bitmap start cluster */ + unsigned int map_sectors; /* num of allocation bitmap sectors */ + struct buffer_head **vol_amap; /* allocation bitmap */ + + unsigned short *vol_utbl; /* upcase table */ + + unsigned int clu_srch_ptr; /* cluster search pointer */ + unsigned int used_clusters; /* number of used clusters */ + + bool s_dirt; + struct mutex s_lock; /* superblock lock */ + struct super_block *host_sb; /* sb pointer */ + struct exfat_mount_options options; + struct nls_table *nls_io; /* Charset used for input and display */ + struct ratelimit_state ratelimit; + + spinlock_t inode_hash_lock; + struct hlist_head inode_hashtable[EXFAT_HASH_SIZE]; +}; + +/* + * EXFAT file system inode in-memory data + */ +struct exfat_inode_info { + struct exfat_chain dir; + int entry; + unsigned int type; + unsigned short attr; + unsigned int start_clu; + unsigned char flags; + /* + * the copy of low 32bit of i_version to check + * the validation of hint_stat. + */ + unsigned int version; + /* file offset or dentry index for readdir */ + loff_t rwoffset; + + /* hint for cluster last accessed */ + struct exfat_hint hint_bmap; + /* hint for entry index we try to lookup next time */ + struct exfat_hint hint_stat; + /* hint for first empty entry */ + struct exfat_hint_femp hint_femp; + + spinlock_t cache_lru_lock; + struct list_head cache_lru; + int nr_caches; + /* for avoiding the race between alloc and free */ + unsigned int cache_valid_id; + + /* + * NOTE: i_size_ondisk is 64bits, so must hold ->inode_lock to access. + * physically allocated size. + */ + loff_t i_size_ondisk; + /* block-aligned i_size (used in cont_write_begin) */ + loff_t i_size_aligned; + /* on-disk position of directory entry or 0 */ + loff_t i_pos; + /* hash by i_location */ + struct hlist_node i_hash_fat; + /* protect bmap against truncate */ + struct rw_semaphore truncate_lock; + struct inode vfs_inode; +}; + +static inline struct exfat_sb_info *EXFAT_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} + +static inline struct exfat_inode_info *EXFAT_I(struct inode *inode) +{ + return container_of(inode, struct exfat_inode_info, vfs_inode); +} + +/* + * If ->i_mode can't hold 0222 (i.e. ATTR_RO), we use ->i_attrs to + * save ATTR_RO instead of ->i_mode. + * + * If it's directory and !sbi->options.rodir, ATTR_RO isn't read-only + * bit, it's just used as flag for app. + */ +static inline int exfat_mode_can_hold_ro(struct inode *inode) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + + if (S_ISDIR(inode->i_mode)) + return 0; + + if ((~sbi->options.fs_fmask) & 0222) + return 1; + return 0; +} + +/* Convert attribute bits and a mask to the UNIX mode. */ +static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi, + unsigned short attr, mode_t mode) +{ + if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR)) + mode &= ~0222; + + if (attr & ATTR_SUBDIR) + return (mode & ~sbi->options.fs_dmask) | S_IFDIR; + + return (mode & ~sbi->options.fs_fmask) | S_IFREG; +} + +/* Return the FAT attribute byte for this inode */ +static inline unsigned short exfat_make_attr(struct inode *inode) +{ + unsigned short attr = EXFAT_I(inode)->attr; + + if (S_ISDIR(inode->i_mode)) + attr |= ATTR_SUBDIR; + if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & 0222)) + attr |= ATTR_READONLY; + return attr; +} + +static inline void exfat_save_attr(struct inode *inode, unsigned short attr) +{ + if (exfat_mode_can_hold_ro(inode)) + EXFAT_I(inode)->attr = attr & (ATTR_RWMASK | ATTR_READONLY); + else + EXFAT_I(inode)->attr = attr & ATTR_RWMASK; +} + +static inline bool exfat_is_last_sector_in_cluster(struct exfat_sb_info *sbi, + sector_t sec) +{ + return ((sec - sbi->data_start_sector + 1) & + ((1 << sbi->sect_per_clus_bits) - 1)) == 0; +} + +static inline sector_t exfat_cluster_to_sector(struct exfat_sb_info *sbi, + unsigned int clus) +{ + return ((clus - BASE_CLUSTER) << sbi->sect_per_clus_bits) + + sbi->data_start_sector; +} + +static inline int exfat_sector_to_cluster(struct exfat_sb_info *sbi, + sector_t sec) +{ + return ((sec - sbi->data_start_sector) >> sbi->sect_per_clus_bits) + + BASE_CLUSTER; +} + +/* super.c */ +int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag); + +/* fatent.c */ +#define exfat_get_next_cluster(sb, pclu) exfat_ent_get(sb, *(pclu), pclu) + +int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, + struct exfat_chain *p_chain); +int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain); +int exfat_ent_get(struct super_block *sb, unsigned int loc, + unsigned int *content); +int exfat_ent_set(struct super_block *sb, unsigned int loc, + unsigned int content); +int exfat_count_ext_entries(struct super_block *sb, struct exfat_chain *p_dir, + int entry, struct exfat_dentry *p_entry); +int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain, + unsigned int len); +struct exfat_dentry *exfat_get_dentry(struct super_block *sb, + struct exfat_chain *p_dir, int entry, struct buffer_head **bh, + sector_t *sector); +struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb, + struct exfat_chain *p_dir, int entry, unsigned int type, + struct exfat_dentry **file_ep); +int exfat_zeroed_cluster(struct inode *dir, unsigned int clu); +int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir, + int entry, sector_t *sector, int *offset); +int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain, + unsigned int *ret_clu); +int exfat_mirror_bh(struct super_block *sb, sector_t sec, + struct buffer_head *bh); +int exfat_count_num_clusters(struct super_block *sb, + struct exfat_chain *p_chain, unsigned int *ret_count); +int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir); + +/* balloc.c */ +int exfat_load_bitmap(struct super_block *sb); +void exfat_free_bitmap(struct super_block *sb); +int exfat_set_bitmap(struct inode *inode, unsigned int clu); +void exfat_clear_bitmap(struct inode *inode, unsigned int clu); +unsigned int exfat_test_bitmap(struct super_block *sb, unsigned int clu); +int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count); + +/* file.c */ +extern const struct file_operations exfat_file_operations; +int __exfat_truncate(struct inode *inode, loff_t new_size); +void exfat_truncate(struct inode *inode, loff_t size); +int exfat_setattr(struct dentry *dentry, struct iattr *attr); +int exfat_getattr(const struct path *path, struct kstat *stat, + unsigned int request_mask, unsigned int query_flags); + +/* namei.c */ +extern const struct dentry_operations exfat_dentry_ops; +extern const struct dentry_operations exfat_ci_dentry_ops; +int exfat_find_empty_entry(struct inode *inode, struct exfat_chain *p_dir, + int num_entries); + +/* cache.c */ +int exfat_cache_init(void); +void exfat_cache_shutdown(void); +void exfat_cache_init_inode(struct inode *inode); +void exfat_cache_inval_inode(struct inode *inode); +int exfat_get_cluster(struct inode *inode, unsigned int cluster, + unsigned int *fclus, unsigned int *dclus, + unsigned int *last_dclus, int allow_eof); + +/* dir.c */ +extern const struct inode_operations exfat_dir_inode_operations; +extern const struct file_operations exfat_dir_operations; +void exfat_get_uniname_from_ext_entry(struct super_block *sb, + struct exfat_chain *p_dir, int entry, unsigned short *uniname); +unsigned int exfat_get_entry_type(struct exfat_dentry *p_entry); +void exfat_get_entry_time(struct exfat_dentry *p_entry, + struct exfat_timestamp *tp, unsigned char mode); +void exfat_set_entry_time(struct exfat_dentry *p_entry, + struct exfat_timestamp *tp, unsigned char mode); +int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir, + int entry, unsigned int type, unsigned int start_clu, + unsigned long long size); +int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir, + int entry, int num_entries, struct exfat_uni_name *p_uniname); +int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir, + int entry, int order, int num_entries); +int update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir, + int entry); +int exfat_update_dir_chksum_with_entry_set(struct super_block *sb, + struct exfat_entry_set_cache *es, int sync); +int exfat_get_num_entries(struct exfat_uni_name *p_uniname); +int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, + struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname, + int num_entries, unsigned int type); +int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu); + +/* inode.c */ +extern const struct inode_operations exfat_file_inode_operations; +void exfat_sync_inode(struct inode *inode); +struct inode *exfat_build_inode(struct super_block *sb, + struct exfat_dir_entry *info, loff_t i_pos); +void exfat_hash_inode(struct inode *inode, loff_t i_pos); +void exfat_unhash_inode(struct inode *inode); +void exfat_truncate(struct inode *inode, loff_t size); +struct inode *exfat_iget(struct super_block *sb, loff_t i_pos); +int exfat_write_inode(struct inode *inode, struct writeback_control *wbc); +void exfat_evict_inode(struct inode *inode); +int exfat_read_inode(struct inode *inode, struct exfat_dir_entry *info); + +/* exfat/nls.c */ +int exfat_nls_cmp_uniname(struct super_block *sb, unsigned short *a, + unsigned short *b); +int exfat_nls_uni16s_to_vfsname(struct super_block *sb, + struct exfat_uni_name *uniname, unsigned char *p_cstring, + int len); +int exfat_nls_vfsname_to_uni16s(struct super_block *sb, + const unsigned char *p_cstring, const int len, + struct exfat_uni_name *uniname, int *p_lossy); +int exfat_create_upcase_table(struct super_block *sb); +void exfat_free_upcase_table(struct super_block *sb); + +/* exfat/misc.c */ +void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...) + __printf(3, 4) __cold; +#define exfat_fs_error(sb, fmt, args...) \ + __exfat_fs_error(sb, 1, fmt, ## args) +#define exfat_fs_error_ratelimit(sb, fmt, args...) \ + __exfat_fs_error(sb, __ratelimit(&EXFAT_SB(sb)->ratelimit), \ + fmt, ## args) +void exfat_msg(struct super_block *sb, const char *lv, const char *fmt, ...) + __printf(3, 4) __cold; +void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec64 *ts, + struct exfat_date_time *tp); +void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec64 *ts, + struct exfat_date_time *tp); +struct exfat_timestamp *exfat_tm_now(struct exfat_sb_info *sbi, + struct exfat_timestamp *tm); +unsigned short exfat_calc_chksum_2byte(void *data, int len, + unsigned short chksum, int type); +void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync); +void exfat_chain_set(struct exfat_chain *ec, unsigned int dir, + unsigned int size, unsigned char flags); +struct exfat_chain *exfat_chain_dup(struct exfat_chain *dir); + +#endif /* !_EXFAT_H */ diff --git a/fs/exfat/exfat_raw.h b/fs/exfat/exfat_raw.h new file mode 100644 index 000000000000..f40603992e0b --- /dev/null +++ b/fs/exfat/exfat_raw.h @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#ifndef _EXFAT_RAW_H +#define _EXFAT_RAW_H + +#include + +#define PBR_SIGNATURE 0xAA55 + +#define VOL_CLEAN 0x0000 +#define VOL_DIRTY 0x0002 + +#define DENTRY_SIZE 32 /* directory entry size */ +#define DENTRY_SIZE_BITS 5 +/* exFAT allows 8388608(256MB) directory entries */ +#define MAX_EXFAT_DENTRIES 8388608 + +/* dentry types */ +#define MSDOS_DELETED 0xE5 /* deleted mark */ +#define MSDOS_UNUSED 0x00 /* end of directory */ + +#define EXFAT_UNUSED 0x00 /* end of directory */ +#define EXFAT_DELETE ~(0x80) +#define IS_EXFAT_DELETED(x) ((x) < 0x80) /* deleted file (0x01~0x7F) */ +#define EXFAT_INVAL 0x80 /* invalid value */ +#define EXFAT_BITMAP 0x81 /* allocation bitmap */ +#define EXFAT_UPCASE 0x82 /* upcase table */ +#define EXFAT_VOLUME 0x83 /* volume label */ +#define EXFAT_FILE 0x85 /* file or dir */ +#define EXFAT_GUID 0xA0 +#define EXFAT_PADDING 0xA1 +#define EXFAT_ACLTAB 0xA2 +#define EXFAT_STREAM 0xC0 /* stream entry */ +#define EXFAT_NAME 0xC1 /* file name entry */ +#define EXFAT_ACL 0xC2 /* stream entry */ + +/* checksum types */ +#define CS_DIR_ENTRY 0 +#define CS_PBR_SECTOR 1 +#define CS_DEFAULT 2 + +/* file attributes */ +#define ATTR_READONLY 0x0001 +#define ATTR_HIDDEN 0x0002 +#define ATTR_SYSTEM 0x0004 +#define ATTR_VOLUME 0x0008 +#define ATTR_SUBDIR 0x0010 +#define ATTR_ARCHIVE 0x0020 +#define ATTR_EXTEND (ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM | \ + ATTR_VOLUME) /* 0x000F */ + +#define ATTR_EXTEND_MASK (ATTR_EXTEND | ATTR_SUBDIR | ATTR_ARCHIVE) +#define ATTR_RWMASK (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \ + ATTR_SUBDIR | ATTR_ARCHIVE) + +#define ATTR_READONLY_LE cpu_to_le16(0x0001) +#define ATTR_HIDDEN_LE cpu_to_le16(0x0002) +#define ATTR_SYSTEM_LE cpu_to_le16(0x0004) +#define ATTR_VOLUME_LE cpu_to_le16(0x0008) +#define ATTR_SUBDIR_LE cpu_to_le16(0x0010) +#define ATTR_ARCHIVE_LE cpu_to_le16(0x0020) + +/* EXFAT BIOS parameter block (64 bytes) */ +struct bpb64 { + __u8 jmp_boot[3]; + __u8 oem_name[8]; + __u8 res_zero[53]; +}; + +/* EXFAT EXTEND BIOS parameter block (56 bytes) */ +struct bsx64 { + __le64 vol_offset; + __le64 vol_length; + __le32 fat_offset; + __le32 fat_length; + __le32 clu_offset; + __le32 clu_count; + __le32 root_cluster; + __le32 vol_serial; + __u8 fs_version[2]; + __le16 vol_flags; + __u8 sect_size_bits; + __u8 sect_per_clus_bits; + __u8 num_fats; + __u8 phy_drv_no; + __u8 perc_in_use; + __u8 reserved2[7]; +}; + +/* EXFAT PBR[BPB+BSX] (120 bytes) */ +struct pbr64 { + struct bpb64 bpb; + struct bsx64 bsx; +}; + +/* Common PBR[Partition Boot Record] (512 bytes) */ +struct pbr { + union { + __u8 raw[64]; + struct bpb64 f64; + } bpb; + union { + __u8 raw[56]; + struct bsx64 f64; + } bsx; + __u8 boot_code[390]; + __le16 signature; +}; + +struct exfat_dentry { + __u8 type; + union { + struct { + __u8 num_ext; + __le16 checksum; + __le16 attr; + __le16 reserved1; + __le16 create_time; + __le16 create_date; + __le16 modify_time; + __le16 modify_date; + __le16 access_time; + __le16 access_date; + __u8 create_time_ms; + __u8 modify_time_ms; + __u8 access_time_ms; + __u8 reserved2[9]; + } __packed file; /* file directory entry */ + struct { + __u8 flags; + __u8 reserved1; + __u8 name_len; + __le16 name_hash; + __le16 reserved2; + __le64 valid_size; + __le32 reserved3; + __le32 start_clu; + __le64 size; + } __packed stream; /* stream extension directory entry */ + struct { + __u8 flags; + __le16 unicode_0_14[15]; + } __packed name; /* file name directory entry */ + struct { + __u8 flags; + __u8 reserved[18]; + __le32 start_clu; + __le64 size; + } __packed bitmap; /* allocation bitmap directory entry */ + struct { + __u8 reserved1[3]; + __le32 checksum; + __u8 reserved2[12]; + __le32 start_clu; + __le64 size; + } __packed upcase; /* up-case table directory entry */ + } __packed dentry; +} __packed; + +#define file_num_ext dentry.file.num_ext +#define file_checksum dentry.file.checksum +#define file_attr dentry.file.attr +#define file_create_time dentry.file.create_time +#define file_create_date dentry.file.create_date +#define file_modify_time dentry.file.modify_time +#define file_modify_date dentry.file.modify_date +#define file_access_time dentry.file.access_time +#define file_access_date dentry.file.access_date +#define file_create_time_ms dentry.file.create_time_ms +#define file_modify_time_ms dentry.file.modify_time_ms +#define file_access_time_ms dentry.file.access_time_ms +#define stream_flags dentry.stream.flags +#define stream_name_len dentry.stream.name_len +#define stream_name_hash dentry.stream.name_hash +#define stream_start_clu dentry.stream.start_clu +#define stream_valid_size dentry.stream.valid_size +#define stream_size dentry.stream.size +#define name_flags dentry.name.flags +#define name_unicode dentry.name.unicode_0_14 +#define bitmap_flags dentry.bitmap.flags +#define bitmap_start_clu dentry.bitmap.start_clu +#define bitmap_size dentry.bitmap.size +#define upcase_start_clu dentry.upcase.start_clu +#define upcase_size dentry.upcase.size +#define upcase_checksum dentry.upcase.checksum + +#endif /* !_EXFAT_RAW_H */ From patchwork Tue Nov 19 09:37:07 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11251643 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 826576C1 for ; Tue, 19 Nov 2019 09:41:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 4700922304 for ; Tue, 19 Nov 2019 09:41:23 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="HX7HwAe+" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727530AbfKSJk1 (ORCPT ); Tue, 19 Nov 2019 04:40:27 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:14015 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727016AbfKSJk1 (ORCPT ); Tue, 19 Nov 2019 04:40:27 -0500 Received: from epcas1p1.samsung.com (unknown [182.195.41.45]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20191119094022epoutp048736ed051a0fb6eed999ea4836499810~Yhu0g-EwM0887308873epoutp04X for ; Tue, 19 Nov 2019 09:40:22 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20191119094022epoutp048736ed051a0fb6eed999ea4836499810~Yhu0g-EwM0887308873epoutp04X DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574156422; bh=smb6XZObye1IE2cVez2iViXnC0ahLBgeI4+As0xoin8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=HX7HwAe+dSXd4F81igfF1xNFebAdzeEUVNZo+DIthwhMkK+O1pgpyrk+gyCpYcdk2 XjVThAkUPAhICMpbvEYyxLfqCkmAD4uCmCcsQv9UBi5dXnVkjnfSjE1ytrZDrwidI7 e21NWs/eRkABtQk6N9EfJWrU0RlEsbh6w42BUV9Q= Received: from epsnrtp4.localdomain (unknown [182.195.42.165]) by epcas1p3.samsung.com (KnoxPortal) with ESMTP id 20191119094022epcas1p39b7c5c2c5fb1d85c058ffc57344880ca~Yhu0MDCAx0934409344epcas1p3V; Tue, 19 Nov 2019 09:40:22 +0000 (GMT) Received: from epsmges1p2.samsung.com (unknown [182.195.40.164]) by epsnrtp4.localdomain (Postfix) with ESMTP id 47HLQs3TJTzMqYkY; Tue, 19 Nov 2019 09:40:21 +0000 (GMT) Received: from epcas1p3.samsung.com ( [182.195.41.47]) by epsmges1p2.samsung.com (Symantec Messaging Gateway) with SMTP id B8.73.04235.588B3DD5; Tue, 19 Nov 2019 18:40:21 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p1.samsung.com (KnoxPortal) with ESMTPA id 20191119094021epcas1p1ef79711e2ea59c4e909a30a9ac2daa3d~Yhuy9HfEH0953109531epcas1p1q; Tue, 19 Nov 2019 09:40:21 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119094021epsmtrp1c5230c726afd9aa47cc3f6cfeef22f10~Yhuy7rrtk0081500815epsmtrp1Z; Tue, 19 Nov 2019 09:40:21 +0000 (GMT) X-AuditID: b6c32a36-defff7000000108b-f6-5dd3b8856d1f Received: from epsmtip2.samsung.com ( [182.195.34.31]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id E1.12.03654.488B3DD5; Tue, 19 Nov 2019 18:40:20 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20191119094020epsmtip21c875f7b258029a3f7ec5fd5af3e6ba3~Yhuyvga6S0597405974epsmtip2c; Tue, 19 Nov 2019 09:40:20 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, Namjae Jeon Subject: [PATCH v3 02/13] exfat: add super block operations Date: Tue, 19 Nov 2019 04:37:07 -0500 Message-Id: <20191119093718.3501-3-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119093718.3501-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA02Se0hTURzHO97t7iqtLsvqsMLWJYkMdXPObul6EzeMkoKCSNZl3py0F7ub ZYUa0RIrtRVF2uxFxWY1USm1lrYp9hA1pUxDIgt74iOXCUZ1t7se/33O7/f9ne+X3zkYIrGj UizHaGUsRlpPoFGCO/4lifFH63sy5YEOKel/6xCRR656UNJV1RpB9g70I+R972MB2dN4ASV/ lb8XkpPnCsi6ny1CsntkVLA6imooHxBRTc6bIupeXyFKldS5AVX79CA1XhND+e5+QalXQ3cE GdhOfZqOobMYi4wxak1ZOcZsNZG+TbNOo0qRK+IVy8llhMxIGxg1sX5TRvyGHD0XkZDl0nob V8qgWZZIXJlmMdmsjExnYq1qgjFn6c0KuTmBpQ2szZidoDUZVijk8iQVp9yt1z1svSAwl18E +z8MeEEhqDoCikEkBvFk2Hd6IMQSvB5AR8eKYhDF8VcAO4+6RPxhAsDbY07hn4lilzvc8AJY O/oR/B2prL/PdTAMxZfCH3WzgwPR+CpYW9EsCGoQvAXAD4EyUbAxC0+F47euIUEW4LGwZmIc DbKYqz897wm7LYBV1c0hTSSeBt9UXA/nbkehO7CH5/Ww2t+G8DwLfmqrE/EshR9L7aE8ED8I x5rCkiIuw3c1z0rY56kWBiUIvgR6GhP58kLYMOUMOSH4DDj87YSQv0UMi+wSXhILS7r9ETzP g8XHRsOmFDzuaAuvpxTAsUK3qAzElP9zuASAG8xhzKwhm2EV5qT/X6wGhP5gXEo9uNKxyQdw DBDTxY8W9WRKhHQum2fwAYghRLR482BXpkScRecdYCwmjcWmZ1gfUHF7PIVIZ2tN3I82WjUK VZJSqSSTU5alqJTEXDE2+SxTgmfTVmYvw5gZy5+5CCxSWghkSL43EJD0Rk87WbXVN+j5ZI/T UOaxy706Sj610XVgx0Rna4J645q1ebHO7eOHXu7qsvcvHL7h6Pyculm+Jv/w0Jl9zsFrzUyr w3lyaJf93rb4kcXv2rrflTSMplfS+a+tjZc0BdG9+INc7Yvn9OvEmV9t7XqH37t9fq7piXbL WULA6mhFHGJh6d8ubXEQmQMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrILMWRmVeSWpSXmKPExsWy7bCSvG7LjsuxBvsabC0OP57EbtG8eD2b xcrVR5ksrt+9xWyxZ+9JFovLu+awWfyf9ZzV4sf0eost/46wWlx6/4HFgctj56y77B77565h 99h9s4HNo2/LKkaPzaerPT5vkvM4tP0Nm8ftZ9tYAjiiuGxSUnMyy1KL9O0SuDIOHp3DUjBr PmPFi7t7GRsYVzczdjFyckgImEh0rVzF3sXIxSEksJtRYu3i3UwQCWmJYyfOMHcxcgDZwhKH DxdD1HxglPj24goTSJxNQFvizxZRkHIRAUeJ3l2HWUBqmAXOMUrsfLYMbIGwgLXE57VLmUFs FgFViU3fPrOB2LxA8dMz17NC7JKXWL3hAFgNp4CNxMPZEL1CQDWNj5rZJzDyLWBkWMUomVpQ nJueW2xYYJiXWq5XnJhbXJqXrpecn7uJERywWpo7GC8viT/EKMDBqMTDe0LlcqwQa2JZcWXu IUYJDmYlEV6/RxdihXhTEiurUovy44tKc1KLDzFKc7AoifM+zTsWKSSQnliSmp2aWpBaBJNl 4uCUamAsOimUm/E8wDw18H8uv1n1DqeI5yvXxl82r3LuqneMXr7hSapH675Su3OXBG69DWY7 N+2Uqej/tJ9uvc4/5V/Mc31ydrvPjW+Jq0udZCMWn5x28OLLsyl9s3K6ljzqCb/uf9z4/ZR8 UYa7lvaB27xSBbbWuaz28zm98tAkJ/dFW+z2OS68K7tfiaU4I9FQi7moOBEAK8qcv1QCAAA= X-CMS-MailID: 20191119094021epcas1p1ef79711e2ea59c4e909a30a9ac2daa3d X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119094021epcas1p1ef79711e2ea59c4e909a30a9ac2daa3d References: <20191119093718.3501-1-namjae.jeon@samsung.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This adds the implementation of superblock operations for exfat. Signed-off-by: Namjae Jeon Signed-off-by: Sungjong Seo --- fs/exfat/super.c | 752 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 752 insertions(+) create mode 100644 fs/exfat/super.c diff --git a/fs/exfat/super.c b/fs/exfat/super.c new file mode 100644 index 000000000000..f7f9bf2cb40d --- /dev/null +++ b/fs/exfat/super.c @@ -0,0 +1,752 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exfat_raw.h" +#include "exfat_fs.h" + +static char exfat_default_iocharset[] = CONFIG_EXFAT_FS_DEFAULT_IOCHARSET; +static const char exfat_iocharset_with_utf8[] = "iso8859-1"; +static struct kmem_cache *exfat_inode_cachep; + +static void exfat_free_iocharset(struct exfat_sb_info *sbi) +{ + if (sbi->options.iocharset != exfat_default_iocharset) + kfree(sbi->options.iocharset); +} + +static void exfat_put_super(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + mutex_lock(&sbi->s_lock); + if (READ_ONCE(sbi->s_dirt)) { + WRITE_ONCE(sbi->s_dirt, false); + sync_blockdev(sb->s_bdev); + } + exfat_set_vol_flags(sb, VOL_CLEAN); + exfat_free_upcase_table(sb); + exfat_free_bitmap(sb); + mutex_unlock(&sbi->s_lock); + + if (sbi->nls_io) { + unload_nls(sbi->nls_io); + sbi->nls_io = NULL; + } + exfat_free_iocharset(sbi); + sb->s_fs_info = NULL; + kfree(sbi); +} + +static int exfat_sync_fs(struct super_block *sb, int wait) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + int err = 0; + + /* If there are some dirty buffers in the bdev inode */ + mutex_lock(&sbi->s_lock); + if (READ_ONCE(sbi->s_dirt)) { + WRITE_ONCE(sbi->s_dirt, false); + sync_blockdev(sb->s_bdev); + if (exfat_set_vol_flags(sb, VOL_CLEAN)) + err = -EIO; + } + mutex_unlock(&sbi->s_lock); + + return err; +} + +static int exfat_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + struct super_block *sb = dentry->d_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + unsigned long long id = huge_encode_dev(sb->s_bdev->bd_dev); + + if (sbi->used_clusters == ~0u) { + mutex_lock(&sbi->s_lock); + if (exfat_count_used_clusters(sb, &sbi->used_clusters)) { + mutex_unlock(&sbi->s_lock); + return -EIO; + } + mutex_unlock(&sbi->s_lock); + } + + buf->f_type = sb->s_magic; + buf->f_bsize = sbi->cluster_size; + buf->f_blocks = sbi->num_clusters - 2; /* clu 0 & 1 */ + buf->f_bfree = buf->f_blocks - sbi->used_clusters; + buf->f_bavail = buf->f_bfree; + buf->f_fsid.val[0] = (unsigned int)id; + buf->f_fsid.val[1] = (unsigned int)(id >> 32); + buf->f_namelen = 260; + + return 0; +} + +static int __exfat_set_vol_flags(struct super_block *sb, + unsigned short new_flag, int always_sync) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct pbr64 *bpb; + int sync = 0; + + /* flags are not changed */ + if (sbi->vol_flag == new_flag) + return 0; + + sbi->vol_flag = new_flag; + + /* skip updating volume dirty flag, + * if this volume has been mounted with read-only + */ + if (sb_rdonly(sb)) + return 0; + + if (!sbi->pbr_bh) { + sbi->pbr_bh = sb_bread(sb, 0); + if (!sbi->pbr_bh) { + exfat_msg(sb, KERN_ERR, "failed to read boot sector"); + return -ENOMEM; + } + } + + bpb = (struct pbr64 *)sbi->pbr_bh->b_data; + bpb->bsx.vol_flags = cpu_to_le16(new_flag); + + if (always_sync) + sync = 1; + else if ((new_flag == VOL_DIRTY) && (!buffer_dirty(sbi->pbr_bh))) + sync = 1; + else + sync = 0; + + set_buffer_uptodate(sbi->pbr_bh); + mark_buffer_dirty(sbi->pbr_bh); + + if (sync) + sync_dirty_buffer(sbi->pbr_bh); + return 0; +} + +int exfat_set_vol_flags(struct super_block *sb, unsigned short new_flag) +{ + return __exfat_set_vol_flags(sb, new_flag, 0); +} + +static int exfat_show_options(struct seq_file *m, struct dentry *root) +{ + struct super_block *sb = root->d_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_mount_options *opts = &sbi->options; + + /* Show partition info */ + if (!uid_eq(opts->fs_uid, GLOBAL_ROOT_UID)) + seq_printf(m, ",uid=%u", + from_kuid_munged(&init_user_ns, opts->fs_uid)); + if (!gid_eq(opts->fs_gid, GLOBAL_ROOT_GID)) + seq_printf(m, ",gid=%u", + from_kgid_munged(&init_user_ns, opts->fs_gid)); + seq_printf(m, ",fmask=%04o,dmask=%04o", opts->fs_fmask, opts->fs_dmask); + if (opts->allow_utime) + seq_printf(m, ",allow_utime=%04o", opts->allow_utime); + if (sbi->nls_io) + seq_printf(m, ",iocharset=%s", sbi->nls_io->charset); + if (opts->utf8) + seq_puts(m, ",utf8"); + seq_printf(m, ",case_sensitive=%u", opts->case_sensitive); + if (opts->tz_utc) + seq_puts(m, ",tz=UTC"); + seq_printf(m, ",bps=%ld", sb->s_blocksize); + if (opts->errors == EXFAT_ERRORS_CONT) + seq_puts(m, ",errors=continue"); + else if (opts->errors == EXFAT_ERRORS_PANIC) + seq_puts(m, ",errors=panic"); + else + seq_puts(m, ",errors=remount-ro"); + if (opts->discard) + seq_puts(m, ",discard"); + + return 0; +} + +static struct inode *exfat_alloc_inode(struct super_block *sb) +{ + struct exfat_inode_info *ei; + + ei = kmem_cache_alloc(exfat_inode_cachep, GFP_NOFS); + if (!ei) + return NULL; + + init_rwsem(&ei->truncate_lock); + return &ei->vfs_inode; +} + +static void exfat_destroy_inode(struct inode *inode) +{ + kmem_cache_free(exfat_inode_cachep, EXFAT_I(inode)); +} + +static const struct super_operations exfat_sops = { + .alloc_inode = exfat_alloc_inode, + .destroy_inode = exfat_destroy_inode, + .write_inode = exfat_write_inode, + .evict_inode = exfat_evict_inode, + .put_super = exfat_put_super, + .sync_fs = exfat_sync_fs, + .statfs = exfat_statfs, + .show_options = exfat_show_options, +}; + +enum { + Opt_uid, + Opt_gid, + Opt_umask, + Opt_dmask, + Opt_fmask, + Opt_allow_utime, + Opt_charset, + Opt_utf8, + Opt_case_sensitive, + Opt_tz, + Opt_errors, + Opt_discard, +}; + +static const struct fs_parameter_spec exfat_param_specs[] = { + fsparam_u32("uid", Opt_uid), + fsparam_u32("gid", Opt_gid), + fsparam_u32oct("umask", Opt_umask), + fsparam_u32oct("dmask", Opt_dmask), + fsparam_u32oct("fmask", Opt_fmask), + fsparam_u32oct("allow_utime", Opt_allow_utime), + fsparam_string("iocharset", Opt_charset), + fsparam_flag("utf8", Opt_utf8), + fsparam_flag("case_sensitive", Opt_case_sensitive), + fsparam_string("tz", Opt_tz), + fsparam_enum("errors", Opt_errors), + fsparam_flag("discard", Opt_discard), + {} +}; + +static const struct fs_parameter_enum exfat_param_enums[] = { + { Opt_errors, "continue", EXFAT_ERRORS_CONT }, + { Opt_errors, "panic", EXFAT_ERRORS_PANIC }, + { Opt_errors, "remount-ro", EXFAT_ERRORS_RO }, + {} +}; + +static const struct fs_parameter_description exfat_parameters = { + .name = "exfat", + .specs = exfat_param_specs, + .enums = exfat_param_enums, +}; + +static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param) +{ + struct exfat_sb_info *sbi = fc->s_fs_info; + struct exfat_mount_options *opts = &sbi->options; + struct fs_parse_result result; + int opt; + + opt = fs_parse(fc, &exfat_parameters, param, &result); + if (opt < 0) + return opt; + + switch (opt) { + case Opt_uid: + opts->fs_uid = make_kuid(current_user_ns(), result.uint_32); + break; + case Opt_gid: + opts->fs_gid = make_kgid(current_user_ns(), result.uint_32); + break; + case Opt_umask: + opts->fs_fmask = result.uint_32; + opts->fs_dmask = result.uint_32; + break; + case Opt_dmask: + opts->fs_dmask = result.uint_32; + break; + case Opt_fmask: + opts->fs_fmask = result.uint_32; + break; + case Opt_allow_utime: + opts->allow_utime = result.uint_32 & 0022; + break; + case Opt_charset: + exfat_free_iocharset(sbi); + opts->iocharset = kstrdup(param->string, GFP_KERNEL); + if (!opts->iocharset) + return -ENOMEM; + break; + case Opt_case_sensitive: + opts->case_sensitive = 1; + break; + case Opt_utf8: + opts->utf8 = 1; + break; + case Opt_tz: + if (!strcmp(param->string, "UTC")) + opts->tz_utc = 1; + break; + case Opt_errors: + opts->errors = result.uint_32; + break; + case Opt_discard: + opts->discard = 1; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void exfat_hash_init(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + int i; + + spin_lock_init(&sbi->inode_hash_lock); + for (i = 0; i < EXFAT_HASH_SIZE; i++) + INIT_HLIST_HEAD(&sbi->inode_hashtable[i]); +} + +static int exfat_read_root(struct inode *inode) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *ei = EXFAT_I(inode); + struct exfat_chain cdir; + int num_subdirs, num_clu = 0; + + exfat_chain_set(&ei->dir, sbi->root_dir, 0, 0x01); + ei->entry = -1; + ei->start_clu = sbi->root_dir; + ei->flags = 0x01; + ei->type = TYPE_DIR; + ei->version = 0; + ei->rwoffset = 0; + ei->hint_bmap.off = EOF_CLUSTER; + ei->hint_stat.eidx = 0; + ei->hint_stat.clu = sbi->root_dir; + ei->hint_femp.eidx = EXFAT_HINT_NONE; + + exfat_chain_set(&cdir, sbi->root_dir, 0, 0x01); + if (exfat_count_num_clusters(sb, &cdir, &num_clu)) + return -EIO; + i_size_write(inode, num_clu << sbi->cluster_size_bits); + + num_subdirs = exfat_count_dir_entries(sb, &cdir); + if (num_subdirs < 0) + return -EIO; + set_nlink(inode, num_subdirs + EXFAT_MIN_SUBDIR); + + inode->i_uid = sbi->options.fs_uid; + inode->i_gid = sbi->options.fs_gid; + inode_inc_iversion(inode); + inode->i_generation = 0; + inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, 0777); + inode->i_op = &exfat_dir_inode_operations; + inode->i_fop = &exfat_dir_operations; + + inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) + & ~(sbi->cluster_size - 1)) >> inode->i_blkbits; + EXFAT_I(inode)->i_pos = ((loff_t)sbi->root_dir << 32) | 0xffffffff; + EXFAT_I(inode)->i_size_aligned = i_size_read(inode); + EXFAT_I(inode)->i_size_ondisk = i_size_read(inode); + + exfat_save_attr(inode, ATTR_SUBDIR); + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + exfat_cache_init_inode(inode); + return 0; +} + +static bool is_exfat(struct pbr *pbr) +{ + int i = 53; + + do { + if (pbr->bpb.f64.res_zero[i - 1]) + break; + } while (--i); + return i ? false : true; +} + +static struct pbr *exfat_read_pbr_with_logical_sector(struct super_block *sb, + struct buffer_head **prev_bh) +{ + struct pbr *p_pbr = (struct pbr *) (*prev_bh)->b_data; + unsigned short logical_sect = 0; + + logical_sect = 1 << p_pbr->bsx.f64.sect_size_bits; + + if (!is_power_of_2(logical_sect) || + logical_sect < 512 || logical_sect > 4096) { + exfat_msg(sb, KERN_ERR, "bogus logical sector size %u", + logical_sect); + return NULL; + } + + if (logical_sect < sb->s_blocksize) { + exfat_msg(sb, KERN_ERR, + "logical sector size too small for device (logical sector size = %u)", + logical_sect); + return NULL; + } + + if (logical_sect > sb->s_blocksize) { + struct buffer_head *bh = NULL; + + __brelse(*prev_bh); + *prev_bh = NULL; + + if (!sb_set_blocksize(sb, logical_sect)) { + exfat_msg(sb, KERN_ERR, + "unable to set blocksize %u", logical_sect); + return NULL; + } + bh = sb_bread(sb, 0); + if (!bh) { + exfat_msg(sb, KERN_ERR, + "unable to read boot sector (logical sector size = %lu)", + sb->s_blocksize); + return NULL; + } + + *prev_bh = bh; + p_pbr = (struct pbr *) bh->b_data; + } + + return p_pbr; +} + +/* mount the file system volume */ +static int __exfat_fill_super(struct super_block *sb) +{ + int ret; + struct pbr *p_pbr; + struct pbr64 *p_bpb; + struct buffer_head *bh; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + /* set block size to read super block */ + sb_min_blocksize(sb, 512); + + /* read boot sector */ + bh = sb_bread(sb, 0); + if (!bh) { + exfat_msg(sb, KERN_ERR, "unable to read boot sector"); + return -EIO; + } + + /* PRB is read */ + p_pbr = (struct pbr *)bh->b_data; + + /* check the validity of PBR */ + if (le16_to_cpu((p_pbr->signature)) != PBR_SIGNATURE) { + exfat_msg(sb, KERN_ERR, "invalid boot record signature"); + ret = -EINVAL; + goto free_bh; + } + + + /* check logical sector size */ + p_pbr = exfat_read_pbr_with_logical_sector(sb, &bh); + if (!p_pbr) { + ret = -EIO; + goto free_bh; + } + + if (!is_exfat(p_pbr)) { + ret = -EINVAL; + goto free_bh; + } + + /* set maximum file size for exFAT */ + sb->s_maxbytes = 0x7fffffffffffffffLL; + + p_bpb = (struct pbr64 *)p_pbr; + if (!p_bpb->bsx.num_fats) { + exfat_msg(sb, KERN_ERR, "bogus number of FAT structure"); + ret = -EINVAL; + goto free_bh; + } + + sbi->sect_per_clus = 1 << p_bpb->bsx.sect_per_clus_bits; + sbi->sect_per_clus_bits = p_bpb->bsx.sect_per_clus_bits; + sbi->cluster_size_bits = sbi->sect_per_clus_bits + sb->s_blocksize_bits; + sbi->cluster_size = 1 << sbi->cluster_size_bits; + sbi->num_FAT_sectors = le32_to_cpu(p_bpb->bsx.fat_length); + sbi->FAT1_start_sector = le32_to_cpu(p_bpb->bsx.fat_offset); + + if (p_bpb->bsx.num_fats == 1) + sbi->FAT2_start_sector = sbi->FAT1_start_sector; + else + sbi->FAT2_start_sector = + sbi->FAT1_start_sector + sbi->num_FAT_sectors; + + sbi->root_start_sector = le32_to_cpu(p_bpb->bsx.clu_offset); + sbi->data_start_sector = sbi->root_start_sector; + sbi->num_sectors = le64_to_cpu(p_bpb->bsx.vol_length); + /* because the cluster index starts with 2 */ + sbi->num_clusters = le32_to_cpu(p_bpb->bsx.clu_count) + 2; + + sbi->vol_id = le32_to_cpu(p_bpb->bsx.vol_serial); + sbi->root_dir = le32_to_cpu(p_bpb->bsx.root_cluster); + sbi->dentries_in_root = 0; + sbi->dentries_per_clu = 1 << + (sbi->cluster_size_bits - DENTRY_SIZE_BITS); + + sbi->vol_flag = le16_to_cpu(p_bpb->bsx.vol_flags); + sbi->clu_srch_ptr = BASE_CLUSTER; + sbi->used_clusters = ~0u; + + if (le16_to_cpu(p_bpb->bsx.vol_flags) & VOL_DIRTY) { + sbi->vol_flag |= VOL_DIRTY; + exfat_msg(sb, KERN_WARNING, + "Volume was not properly unmounted. Some data may be corrupt. Please run fsck."); + } + + ret = exfat_create_upcase_table(sb); + if (ret) { + exfat_msg(sb, KERN_ERR, "failed to load upcase table"); + goto free_bh; + } + + /* allocate-bitmap is only for exFAT */ + ret = exfat_load_bitmap(sb); + if (ret) { + exfat_msg(sb, KERN_ERR, "failed to load alloc-bitmap"); + goto free_upcase; + } + + ret = exfat_count_used_clusters(sb, &sbi->used_clusters); + if (ret) { + exfat_msg(sb, KERN_ERR, "failed to scan clusters"); + goto free_alloc_bitmap; + } + + return 0; + +free_alloc_bitmap: + exfat_free_bitmap(sb); +free_upcase: + exfat_free_upcase_table(sb); +free_bh: + brelse(bh); + + return ret; +} + +static int exfat_fill_super(struct super_block *sb, struct fs_context *fc) +{ + struct exfat_sb_info *sbi = sb->s_fs_info; + struct exfat_mount_options *opts = &sbi->options; + struct inode *root_inode; + int err; + + if (opts->allow_utime == -1) + opts->allow_utime = ~opts->fs_dmask & 0022; + + if (opts->utf8 && strcmp(opts->iocharset, exfat_iocharset_with_utf8)) { + exfat_msg(sb, KERN_WARNING, + "utf8 enabled, \"iocharset=%s\" is recommended", + exfat_iocharset_with_utf8); + } + + if (opts->discard) { + struct request_queue *q = bdev_get_queue(sb->s_bdev); + + if (!blk_queue_discard(q)) + exfat_msg(sb, KERN_WARNING, + "mounting with \"discard\" option, but the device does not support discard"); + opts->discard = 0; + } + + sb->s_flags |= SB_NODIRATIME; + sb->s_magic = EXFAT_SUPER_MAGIC; + sb->s_op = &exfat_sops; + + if (EXFAT_SB(sb)->options.case_sensitive) + sb->s_d_op = &exfat_dentry_ops; + else + sb->s_d_op = &exfat_ci_dentry_ops; + + err = __exfat_fill_super(sb); + if (err) { + exfat_msg(sb, KERN_ERR, "failed to recognize exfat type"); + goto failed_mount; + } + + /* set up enough so that it can read an inode */ + exfat_hash_init(sb); + + sbi->nls_io = load_nls(sbi->options.iocharset); + if (!sbi->nls_io) { + exfat_msg(sb, KERN_ERR, "IO charset %s not found", + sbi->options.iocharset); + err = -EINVAL; + goto failed_mount2; + } + + root_inode = new_inode(sb); + if (!root_inode) { + exfat_msg(sb, KERN_ERR, "failed to allocate root inode."); + err = -ENOMEM; + goto failed_mount2; + } + + root_inode->i_ino = EXFAT_ROOT_INO; + inode_set_iversion(root_inode, 1); + err = exfat_read_root(root_inode); + if (err) { + exfat_msg(sb, KERN_ERR, "failed to initialize root inode."); + goto failed_mount3; + } + + exfat_hash_inode(root_inode, EXFAT_I(root_inode)->i_pos); + insert_inode_hash(root_inode); + + sb->s_root = d_make_root(root_inode); + if (!sb->s_root) { + exfat_msg(sb, KERN_ERR, "failed to get the root dentry"); + err = -ENOMEM; + goto failed_mount3; + } + + return 0; + +failed_mount3: + iput(root_inode); + sb->s_root = NULL; + +failed_mount2: + exfat_free_upcase_table(sb); + exfat_free_bitmap(sb); + +failed_mount: + if (sbi->nls_io) + unload_nls(sbi->nls_io); + exfat_free_iocharset(sbi); + sb->s_fs_info = NULL; + kfree(sbi); + return err; +} + +static int exfat_get_tree(struct fs_context *fc) +{ + return get_tree_bdev(fc, exfat_fill_super); +} + +static void exfat_free(struct fs_context *fc) +{ + kfree(fc->s_fs_info); +} + +static const struct fs_context_operations exfat_context_ops = { + .parse_param = exfat_parse_param, + .get_tree = exfat_get_tree, + .free = exfat_free, +}; + +static int exfat_init_fs_context(struct fs_context *fc) +{ + struct exfat_sb_info *sbi; + + sbi = kzalloc(sizeof(struct exfat_sb_info), GFP_KERNEL); + if (!sbi) + return -ENOMEM; + + mutex_init(&sbi->s_lock); + ratelimit_state_init(&sbi->ratelimit, DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + + sbi->options.fs_uid = current_uid(); + sbi->options.fs_gid = current_gid(); + sbi->options.fs_fmask = current->fs->umask; + sbi->options.fs_dmask = current->fs->umask; + sbi->options.allow_utime = -1; + sbi->options.iocharset = exfat_default_iocharset; + sbi->options.errors = EXFAT_ERRORS_RO; + + fc->s_fs_info = sbi; + fc->ops = &exfat_context_ops; + return 0; +} + +static struct file_system_type exfat_fs_type = { + .owner = THIS_MODULE, + .name = "exfat", + .init_fs_context = exfat_init_fs_context, + .parameters = &exfat_parameters, + .kill_sb = kill_block_super, + .fs_flags = FS_REQUIRES_DEV, +}; + +static void exfat_inode_init_once(void *foo) +{ + struct exfat_inode_info *ei = (struct exfat_inode_info *)foo; + + INIT_HLIST_NODE(&ei->i_hash_fat); + inode_init_once(&ei->vfs_inode); +} + +static int __init init_exfat_fs(void) +{ + int err; + + err = exfat_cache_init(); + if (err) + return err; + + err = -ENOMEM; + exfat_inode_cachep = kmem_cache_create("exfat_inode_cache", + sizeof(struct exfat_inode_info), + 0, SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, + exfat_inode_init_once); + if (!exfat_inode_cachep) + goto shutdown_cache; + + err = register_filesystem(&exfat_fs_type); + if (err) + goto destroy_cache; + + return 0; + +destroy_cache: + kmem_cache_destroy(exfat_inode_cachep); +shutdown_cache: + exfat_cache_shutdown(); + + return err; +} + +static void __exit exit_exfat_fs(void) +{ + kmem_cache_destroy(exfat_inode_cachep); + unregister_filesystem(&exfat_fs_type); + exfat_cache_shutdown(); +} + +module_init(init_exfat_fs); +module_exit(exit_exfat_fs); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("exFAT filesystem support"); +MODULE_AUTHOR("Samsung Electronics Co., Ltd."); From patchwork Tue Nov 19 09:37:08 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11251633 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B5EA914C0 for ; Tue, 19 Nov 2019 09:41:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 531B922312 for ; Tue, 19 Nov 2019 09:41:07 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="WcHIBBBh" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727767AbfKSJlE (ORCPT ); Tue, 19 Nov 2019 04:41:04 -0500 Received: from mailout2.samsung.com ([203.254.224.25]:40652 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727409AbfKSJk3 (ORCPT ); Tue, 19 Nov 2019 04:40:29 -0500 Received: from epcas1p1.samsung.com (unknown [182.195.41.45]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20191119094023epoutp028b8fa36dc0bdfb678d56e4a9e54cc3ab~Yhu1PPjWl0315703157epoutp020 for ; Tue, 19 Nov 2019 09:40:23 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20191119094023epoutp028b8fa36dc0bdfb678d56e4a9e54cc3ab~Yhu1PPjWl0315703157epoutp020 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574156423; bh=jRWJVgZ+/q6pMFVbl44kxkarbxPHXt0FS3gZATmXIew=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=WcHIBBBhctnkZe1ZvlRiBIW9WEXzDpP4vkqMKbnQfsn4TI49PO6LUoxPcrKJABdX7 Ofmts8V1xpIzTBCzelHYsBl8psHW00xfnIxQp6m4327q4GZgPSO49kyuX3+ipk51/f tdvox3icGQ15GVI+x1ysYLOO8u6S+r5Ojer5mX/A= Received: from epsnrtp3.localdomain (unknown [182.195.42.164]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20191119094023epcas1p17c565b1ebeae5f8babd1557e3ff1d515~Yhu00GKes0953509535epcas1p1p; Tue, 19 Nov 2019 09:40:23 +0000 (GMT) Received: from epsmges1p4.samsung.com (unknown [182.195.40.162]) by epsnrtp3.localdomain (Postfix) with ESMTP id 47HLQt0qNkzMqYkf; Tue, 19 Nov 2019 09:40:22 +0000 (GMT) Received: from epcas1p1.samsung.com ( [182.195.41.45]) by epsmges1p4.samsung.com (Symantec Messaging Gateway) with SMTP id 7F.94.04406.688B3DD5; Tue, 19 Nov 2019 18:40:22 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p1.samsung.com (KnoxPortal) with ESMTPA id 20191119094021epcas1p16e9ebb9fd8a1b25c64b09899a31988b9~YhuzlEYtT1823218232epcas1p1B; Tue, 19 Nov 2019 09:40:21 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119094021epsmtrp1870686d7a059594b332ee117996e58a1~YhuzjcVed0080100801epsmtrp1l; Tue, 19 Nov 2019 09:40:21 +0000 (GMT) X-AuditID: b6c32a38-95fff70000001136-2e-5dd3b8862f25 Received: from epsmtip2.samsung.com ( [182.195.34.31]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id AC.92.03814.588B3DD5; Tue, 19 Nov 2019 18:40:21 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20191119094021epsmtip21b9e1f906442f3d517d47e0559595f2d~YhuzW6uCM0708907089epsmtip2C; Tue, 19 Nov 2019 09:40:21 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, Namjae Jeon Subject: [PATCH v3 03/13] exfat: add inode operations Date: Tue, 19 Nov 2019 04:37:08 -0500 Message-Id: <20191119093718.3501-4-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119093718.3501-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFmplk+LIzCtJLcpLzFFi42LZdlhTV7dtx+VYgy33WSwOP57EbtG8eD2b xcrVR5ksrt+9xWyxZ+9JFovLu+awWfyf9ZzV4sf0eost/46wWlx6/4HFgctj56y77B77565h 99h9s4HNo2/LKkaPzaerPT5vkvM4tP0Nm8ftZ9tYAjiicmwyUhNTUosUUvOS81My89JtlbyD 453jTc0MDHUNLS3MlRTyEnNTbZVcfAJ03TJzgE5UUihLzCkFCgUkFhcr6dvZFOWXlqQqZOQX l9gqpRak5BQYGhToFSfmFpfmpesl5+daGRoYGJkCVSbkZPRunM5ccOs+c8XraZdYGhj3NjJ3 MXJwSAiYSDx8YN3FyMUhJLCDUeLApg9MEM4nRokNE2+zQDjfGCX2ftrD2MXICdbR37gOKrGX UeJF82s2uJZDU+azgcxlE9CW+LNFFKRBRMBeYvPsA2ANzAJHgBq+TGAHqREWMJU4/l8RpIZF QFXizY5VrCA2r4C1xMal+5gglslLrN5wgBnE5hSwkXg4exnUEWfYJBZ85ICwXSSuzT7CDGEL S7w6voUdwpaSeNnfxg7xZrXEx/1QJR1AJ3y3hbCNJW6u38AKUsIsoCmxfpc+RFhRYufvuWCb mAX4JN597WGFmMIr0dEmBFGiKtF36TDUkdISXe0foJZ6SBy4cp4REiD9jBLbHr1kncAoNwth wwJGxlWMYqkFxbnpqcWGBSbIEbaJEZwEtSx2MO4553OIUYCDUYmH94TK5Vgh1sSy4srcQ4wS HMxKIrx+jy7ECvGmJFZWpRblxxeV5qQWH2I0BYbjRGYp0eR8YILOK4k3NDUyNja2MDEzNzM1 VhLn5fhxMVZIID2xJDU7NbUgtQimj4mDU6qBsdI27FD9FPYPxRbsTy/e68hkPzShvM9GZpLW SpclNm05/slLzl6rOnvux+NjP8OPPfT1+fTf1CHwdwXrFAmTbyHF2YGXDYPU69xkLyy2NVy4 z3PDd/dqwcvfZzhf65sqb8a0zI5R7nJO0uQPE75dWbTrhYev0alKnza+pe/e601pXHXT7nu9 hRJLcUaioRZzUXEiAO3q2TaYAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrALMWRmVeSWpSXmKPExsWy7bCSvG7rjsuxBhOPalocfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9RZb/h1htbj0/gOLA5fHzll32T32z13D 7rH7ZgObR9+WVYwem09Xe3zeJOdxaPsbNo/bz7axBHBEcdmkpOZklqUW6dslcGX0bpzOXHDr PnPF62mXWBoY9zYydzFyckgImEj0N65j6WLk4hAS2M0osWrlUVaIhLTEsRNngIo4gGxhicOH iyFqPjBKtGxpZAeJswloS/zZIgpSLiLgKNG76zDYHGaBc4wSO58tYwSpERYwlTj+XxGkhkVA VeLNjlVg43kFrCU2Lt3HBLFKXmL1hgNg93AK2Eg8nA3Sygm0y1qi8VEz+wRGvgWMDKsYJVML inPTc4sNC4zyUsv1ihNzi0vz0vWS83M3MYLDVUtrB+OJE/GHGAU4GJV4eE+oXI4VYk0sK67M PcQowcGsJMLr9+hCrBBvSmJlVWpRfnxRaU5q8SFGaQ4WJXFe+fxjkUIC6YklqdmpqQWpRTBZ Jg5OqQbG4BlebfP5zmV9u99yaCXr5a+R71+FPFrwLqr2Uf6KOYflFOd/WSi05kV5UbRmk1NW 4kVdJ6lbK8Od593s/redT+fG78NH0uYsPX/fdXmZbkwTU/OU30XVGwzXXlix0za09Nlhxq+l yfUbX13NEK89nvWFo9qcwcZ0/RPOXf+aC4Q3XvlkIlYgq8RSnJFoqMVcVJwIAIv1V05TAgAA X-CMS-MailID: 20191119094021epcas1p16e9ebb9fd8a1b25c64b09899a31988b9 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119094021epcas1p16e9ebb9fd8a1b25c64b09899a31988b9 References: <20191119093718.3501-1-namjae.jeon@samsung.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This adds the implementation of inode operations for exfat. Signed-off-by: Namjae Jeon Signed-off-by: Sungjong Seo --- fs/exfat/inode.c | 691 +++++++++++++++++++++ fs/exfat/namei.c | 1492 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2183 insertions(+) create mode 100644 fs/exfat/inode.c create mode 100644 fs/exfat/namei.c diff --git a/fs/exfat/inode.c b/fs/exfat/inode.c new file mode 100644 index 000000000000..053618545adf --- /dev/null +++ b/fs/exfat/inode.c @@ -0,0 +1,691 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exfat_raw.h" +#include "exfat_fs.h" + +/* 2-level option flag */ +#define BMAP_NOT_CREATE 0 +#define BMAP_ADD_CLUSTER 1 + +static int __exfat_write_inode(struct inode *inode, int sync) +{ + int ret = -EIO; + unsigned long long on_disk_size; + struct exfat_timestamp tm; + struct exfat_dentry *ep, *ep2; + struct exfat_entry_set_cache *es = NULL; + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *ei = EXFAT_I(inode); + unsigned char is_dir = (ei->type == TYPE_DIR) ? 1 : 0; + struct exfat_dir_entry info; + + if (inode->i_ino == EXFAT_ROOT_INO) + return 0; + + info.attr = exfat_make_attr(inode); + info.size = i_size_read(inode); + + exfat_time_unix2fat(sbi, &inode->i_mtime, &info.modify_timestamp); + exfat_time_unix2fat(sbi, &inode->i_ctime, &info.create_timestamp); + exfat_time_unix2fat(sbi, &inode->i_atime, &info.access_timestamp); + + /* + * If the indode is already unlinked, there is no need for updating it. + */ + if (ei->dir.dir == DIR_DELETED) + return 0; + + if (is_dir && ei->dir.dir == sbi->root_dir && ei->entry == -1) + return 0; + + exfat_set_vol_flags(sb, VOL_DIRTY); + + /* get the directory entry of given file or directory */ + es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry, ES_ALL_ENTRIES, + &ep); + if (!es) + return -EIO; + ep2 = ep + 1; + + ep->file_attr = cpu_to_le16(info.attr); + + /* set FILE_INFO structure using the acquired struct exfat_dentry */ + tm.sec = info.create_timestamp.second; + tm.min = info.create_timestamp.minute; + tm.hour = info.create_timestamp.hour; + tm.day = info.create_timestamp.day; + tm.mon = info.create_timestamp.month; + tm.year = info.create_timestamp.year; + exfat_set_entry_time(ep, &tm, TM_CREATE); + + tm.sec = info.modify_timestamp.second; + tm.min = info.modify_timestamp.minute; + tm.hour = info.modify_timestamp.hour; + tm.day = info.modify_timestamp.day; + tm.mon = info.modify_timestamp.month; + tm.year = info.modify_timestamp.year; + exfat_set_entry_time(ep, &tm, TM_MODIFY); + + /* File size should be zero if there is no cluster allocated */ + on_disk_size = info.size; + + if (ei->start_clu == EOF_CLUSTER) + on_disk_size = 0; + + ep2->stream_valid_size = cpu_to_le64(on_disk_size); + ep2->stream_size = ep2->stream_valid_size; + + ret = exfat_update_dir_chksum_with_entry_set(sb, es, sync); + kfree(es); + return ret; +} + +int exfat_write_inode(struct inode *inode, struct writeback_control *wbc) +{ + int ret; + + mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock); + ret = __exfat_write_inode(inode, wbc->sync_mode == WB_SYNC_ALL); + mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock); + + return ret; +} + +void exfat_sync_inode(struct inode *inode) +{ + lockdep_assert_held(&EXFAT_SB(inode->i_sb)->s_lock); + __exfat_write_inode(inode, 1); +} + +/* + * Input: inode, (logical) clu_offset, target allocation area + * Output: errcode, cluster number + * *clu = (~0), if it's unable to allocate a new cluster + */ +static int exfat_map_cluster(struct inode *inode, unsigned int clu_offset, + unsigned int *clu, int create) +{ + int ret, modified = false; + unsigned int last_clu; + struct exfat_chain new_clu; + struct exfat_dentry *ep; + struct exfat_entry_set_cache *es = NULL; + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *ei = EXFAT_I(inode); + unsigned int local_clu_offset = clu_offset; + unsigned int num_to_be_allocated = 0, num_clusters = 0; + + ei->rwoffset = EXFAT_CLU_TO_B(clu_offset, sbi); + + if (EXFAT_I(inode)->i_size_ondisk > 0) + num_clusters = + EXFAT_B_TO_CLU_ROUND_UP(EXFAT_I(inode)->i_size_ondisk, + sbi); + + if (clu_offset >= num_clusters) + num_to_be_allocated = clu_offset - num_clusters + 1; + + if (!create && (num_to_be_allocated > 0)) { + *clu = EOF_CLUSTER; + return 0; + } + + *clu = last_clu = ei->start_clu; + + if (ei->flags == 0x03) { + if (clu_offset > 0 && *clu != EOF_CLUSTER) { + last_clu += clu_offset - 1; + + if (clu_offset == num_clusters) + *clu = EOF_CLUSTER; + else + *clu += clu_offset; + } + } else if (ei->type == TYPE_FILE) { + unsigned int fclus = 0; + int err = exfat_get_cluster(inode, clu_offset, + &fclus, clu, &last_clu, 1); + if (err) + return -EIO; + + clu_offset -= fclus; + } else { + /* hint information */ + if (clu_offset > 0 && ei->hint_bmap.off != EOF_CLUSTER && + ei->hint_bmap.off > 0 && clu_offset >= ei->hint_bmap.off) { + clu_offset -= ei->hint_bmap.off; + /* hint_bmap.clu should be valid */ + WARN_ON(ei->hint_bmap.clu < 2); + *clu = ei->hint_bmap.clu; + } + + while (clu_offset > 0 && *clu != EOF_CLUSTER) { + last_clu = *clu; + if (exfat_get_next_cluster(sb, clu)) + return -EIO; + clu_offset--; + } + } + + if (*clu == EOF_CLUSTER) { + exfat_set_vol_flags(sb, VOL_DIRTY); + + new_clu.dir = (last_clu == EOF_CLUSTER) ? + EOF_CLUSTER : last_clu + 1; + new_clu.size = 0; + new_clu.flags = ei->flags; + + /* allocate a cluster */ + if (num_to_be_allocated < 1) { + /* Broken FAT (i_sze > allocated FAT) */ + exfat_fs_error(sb, "broken FAT chain."); + return -EIO; + } + + ret = exfat_alloc_cluster(inode, num_to_be_allocated, &new_clu); + if (ret) + return ret; + + if (new_clu.dir == EOF_CLUSTER || new_clu.dir == FREE_CLUSTER) { + exfat_fs_error(sb, + "bogus cluster new allocated (last_clu : %u, new_clu : %u)", + last_clu, new_clu.dir); + return -EIO; + } + + /* append to the FAT chain */ + if (last_clu == EOF_CLUSTER) { + if (new_clu.flags == 0x01) + ei->flags = 0x01; + ei->start_clu = new_clu.dir; + modified = true; + } else { + if (new_clu.flags != ei->flags) { + /* no-fat-chain bit is disabled, + * so fat-chain should be synced with + * alloc-bitmap + */ + exfat_chain_cont_cluster(sb, ei->start_clu, + num_clusters); + ei->flags = 0x01; + modified = true; + } + if (new_clu.flags == 0x01) + if (exfat_ent_set(sb, last_clu, new_clu.dir)) + return -EIO; + } + + num_clusters += num_to_be_allocated; + *clu = new_clu.dir; + + if (ei->dir.dir != DIR_DELETED) { + es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry, + ES_ALL_ENTRIES, &ep); + if (!es) + return -EIO; + /* get stream entry */ + ep++; + + /* update directory entry */ + if (modified) { + if (ep->stream_flags != ei->flags) + ep->stream_flags = ei->flags; + + if (le32_to_cpu(ep->stream_start_clu) != + ei->start_clu) + ep->stream_start_clu = + cpu_to_le32(ei->start_clu); + + ep->stream_valid_size = + cpu_to_le64(i_size_read(inode)); + ep->stream_size = ep->stream_valid_size; + } + + if (exfat_update_dir_chksum_with_entry_set(sb, es, + inode_needs_sync(inode))) + return -EIO; + kfree(es); + + } /* end of if != DIR_DELETED */ + + inode->i_blocks += + num_to_be_allocated << sbi->sect_per_clus_bits; + + /* + * Move *clu pointer along FAT chains (hole care) because the + * caller of this function expect *clu to be the last cluster. + * This only works when num_to_be_allocated >= 2, + * *clu = (the first cluster of the allocated chain) => + * (the last cluster of ...) + */ + if (ei->flags == 0x03) { + *clu += num_to_be_allocated - 1; + } else { + while (num_to_be_allocated > 1) { + if (exfat_get_next_cluster(sb, clu)) + return -EIO; + num_to_be_allocated--; + } + } + + } + + /* hint information */ + ei->hint_bmap.off = local_clu_offset; + ei->hint_bmap.clu = *clu; + + return 0; +} + +static int exfat_bmap(struct inode *inode, sector_t sector, sector_t *phys, + unsigned long *mapped_blocks, int *create) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + sector_t last_block; + unsigned int cluster, clu_offset, sec_offset; + int err = 0; + + *phys = 0; + *mapped_blocks = 0; + + last_block = EXFAT_B_TO_BLK_ROUND_UP(i_size_read(inode), sb); + if (sector >= last_block && *create == BMAP_NOT_CREATE) + return 0; + + /* Is this block already allocated? */ + clu_offset = sector >> sbi->sect_per_clus_bits; /* cluster offset */ + + err = exfat_map_cluster(inode, clu_offset, &cluster, + *create & BMAP_ADD_CLUSTER); + if (err) { + if (err != -ENOSPC) + return -EIO; + return err; + } + + if (cluster != EOF_CLUSTER) { + /* sector offset in cluster */ + sec_offset = sector & (sbi->sect_per_clus - 1); + + *phys = exfat_cluster_to_sector(sbi, cluster) + sec_offset; + *mapped_blocks = sbi->sect_per_clus - sec_offset; + } + + if (sector < last_block) + *create = BMAP_NOT_CREATE; + return 0; +} + +static int exfat_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create) +{ + struct super_block *sb = inode->i_sb; + unsigned long max_blocks = bh_result->b_size >> inode->i_blkbits; + int err = 0; + unsigned long mapped_blocks; + sector_t phys; + loff_t pos; + int bmap_create = create ? BMAP_ADD_CLUSTER : BMAP_NOT_CREATE; + + mutex_lock(&EXFAT_SB(sb)->s_lock); + err = exfat_bmap(inode, iblock, &phys, &mapped_blocks, &bmap_create); + if (err) { + if (err != -ENOSPC) + exfat_fs_error_ratelimit(sb, + "failed to bmap (inode : %p iblock : %llu, err : %d)", + inode, (unsigned long long)iblock, err); + goto unlock_ret; + } + + if (phys) { + max_blocks = min(mapped_blocks, max_blocks); + + /* Treat newly added block / cluster */ + if (bmap_create || buffer_delay(bh_result)) { + /* Update i_size_ondisk */ + pos = EXFAT_BLK_TO_B((iblock + 1), sb); + if (EXFAT_I(inode)->i_size_ondisk < pos) + EXFAT_I(inode)->i_size_ondisk = pos; + + if (bmap_create) { + if (buffer_delay(bh_result) && + pos > EXFAT_I(inode)->i_size_aligned) { + exfat_fs_error(sb, + "requested for bmap out of range(pos : (%llu) > i_size_aligned(%llu)\n", + pos, + EXFAT_I(inode)->i_size_aligned); + err = -EIO; + goto unlock_ret; + } + set_buffer_new(bh_result); + + /* + * adjust i_size_aligned if i_size_ondisk is + * bigger than it. (i.e. non-DA) + */ + if (EXFAT_I(inode)->i_size_ondisk > + EXFAT_I(inode)->i_size_aligned) { + EXFAT_I(inode)->i_size_aligned = + EXFAT_I(inode)->i_size_ondisk; + } + } + + if (buffer_delay(bh_result)) + clear_buffer_delay(bh_result); + } + map_bh(bh_result, sb, phys); + } + + bh_result->b_size = EXFAT_BLK_TO_B(max_blocks, sb); +unlock_ret: + mutex_unlock(&EXFAT_SB(sb)->s_lock); + return err; +} + +static int exfat_readpage(struct file *file, struct page *page) +{ + return mpage_readpage(page, exfat_get_block); +} + +static int exfat_readpages(struct file *file, struct address_space *mapping, + struct list_head *pages, unsigned int nr_pages) +{ + return mpage_readpages(mapping, pages, nr_pages, exfat_get_block); +} + +static int exfat_writepage(struct page *page, struct writeback_control *wbc) +{ + return block_write_full_page(page, exfat_get_block, wbc); +} + +static int exfat_writepages(struct address_space *mapping, + struct writeback_control *wbc) +{ + return mpage_writepages(mapping, wbc, exfat_get_block); +} + +static void exfat_write_failed(struct address_space *mapping, loff_t to) +{ + struct inode *inode = mapping->host; + + if (to > i_size_read(inode)) { + truncate_pagecache(inode, i_size_read(inode)); + exfat_truncate(inode, EXFAT_I(inode)->i_size_aligned); + } +} + +static int exfat_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned int len, unsigned int flags, + struct page **pagep, void **fsdata) +{ + int ret; + + *pagep = NULL; + ret = cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, + exfat_get_block, + &EXFAT_I(mapping->host)->i_size_ondisk); + + if (ret < 0) + exfat_write_failed(mapping, pos+len); + + return ret; +} + +static int exfat_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned int len, unsigned int copied, + struct page *pagep, void *fsdata) +{ + struct inode *inode = mapping->host; + struct exfat_inode_info *ei = EXFAT_I(inode); + int err; + + err = generic_write_end(file, mapping, pos, len, copied, pagep, fsdata); + + if (EXFAT_I(inode)->i_size_aligned < i_size_read(inode)) { + exfat_fs_error(inode->i_sb, + "invalid size(size(%llu) > aligned(%llu)\n", + i_size_read(inode), EXFAT_I(inode)->i_size_aligned); + return -EIO; + } + + if (err < len) + exfat_write_failed(mapping, pos+len); + + if (!(err < 0) && !(ei->attr & ATTR_ARCHIVE)) { + inode->i_mtime = inode->i_ctime = current_time(inode); + ei->attr |= ATTR_ARCHIVE; + mark_inode_dirty(inode); + } + + return err; +} + +static ssize_t exfat_direct_IO(struct kiocb *iocb, struct iov_iter *iter) +{ + struct address_space *mapping = iocb->ki_filp->f_mapping; + struct inode *inode = mapping->host; + loff_t size = iocb->ki_pos + iov_iter_count(iter); + int rw = iov_iter_rw(iter); + ssize_t ret; + + if (rw == WRITE) { + /* + * FIXME: blockdev_direct_IO() doesn't use ->write_begin(), + * so we need to update the ->i_size_aligned to block boundary. + * + * But we must fill the remaining area or hole by nul for + * updating ->i_size_aligned + * + * Return 0, and fallback to normal buffered write. + */ + if (EXFAT_I(inode)->i_size_aligned < size) + return 0; + } + + /* + * Need to use the DIO_LOCKING for avoiding the race + * condition of exfat_get_block() and ->truncate(). + */ + ret = blockdev_direct_IO(iocb, inode, iter, exfat_get_block); + if (ret < 0 && (rw & WRITE)) + exfat_write_failed(mapping, size); + return ret; +} + +static sector_t exfat_aop_bmap(struct address_space *mapping, sector_t block) +{ + sector_t blocknr; + + /* exfat_get_cluster() assumes the requested blocknr isn't truncated. */ + down_read(&EXFAT_I(mapping->host)->truncate_lock); + blocknr = generic_block_bmap(mapping, block, exfat_get_block); + up_read(&EXFAT_I(mapping->host)->truncate_lock); + return blocknr; +} + +static const struct address_space_operations exfat_aops = { + .readpage = exfat_readpage, + .readpages = exfat_readpages, + .writepage = exfat_writepage, + .writepages = exfat_writepages, + .write_begin = exfat_write_begin, + .write_end = exfat_write_end, + .direct_IO = exfat_direct_IO, + .bmap = exfat_aop_bmap +}; + +static inline unsigned long exfat_hash(loff_t i_pos) +{ + return hash_32(i_pos, EXFAT_HASH_BITS); +} + +void exfat_hash_inode(struct inode *inode, loff_t i_pos) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); + + spin_lock(&sbi->inode_hash_lock); + EXFAT_I(inode)->i_pos = i_pos; + hlist_add_head(&EXFAT_I(inode)->i_hash_fat, head); + spin_unlock(&sbi->inode_hash_lock); +} + +void exfat_unhash_inode(struct inode *inode) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + + spin_lock(&sbi->inode_hash_lock); + hlist_del_init(&EXFAT_I(inode)->i_hash_fat); + EXFAT_I(inode)->i_pos = 0; + spin_unlock(&sbi->inode_hash_lock); +} + +struct inode *exfat_iget(struct super_block *sb, loff_t i_pos) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *info; + struct hlist_head *head = sbi->inode_hashtable + exfat_hash(i_pos); + struct inode *inode = NULL; + + spin_lock(&sbi->inode_hash_lock); + hlist_for_each_entry(info, head, i_hash_fat) { + WARN_ON(info->vfs_inode.i_sb != sb); + + if (i_pos != info->i_pos) + continue; + inode = igrab(&info->vfs_inode); + if (inode) + break; + } + spin_unlock(&sbi->inode_hash_lock); + return inode; +} + +/* doesn't deal with root inode */ +static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info) +{ + struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb); + struct exfat_inode_info *ei = EXFAT_I(inode); + loff_t size = info->size; + + memcpy(&ei->dir, &info->dir, sizeof(struct exfat_chain)); + ei->entry = info->entry; + ei->attr = info->attr; + ei->start_clu = info->start_clu; + ei->flags = info->flags; + ei->type = info->type; + + ei->version = 0; + ei->hint_stat.eidx = 0; + ei->hint_stat.clu = info->start_clu; + ei->hint_femp.eidx = EXFAT_HINT_NONE; + ei->rwoffset = 0; + ei->hint_bmap.off = EOF_CLUSTER; + ei->i_pos = 0; + + inode->i_uid = sbi->options.fs_uid; + inode->i_gid = sbi->options.fs_gid; + inode_inc_iversion(inode); + inode->i_generation = prandom_u32(); + + if (info->attr & ATTR_SUBDIR) { /* directory */ + inode->i_generation &= ~1; + inode->i_mode = exfat_make_mode(sbi, info->attr, 0777); + inode->i_op = &exfat_dir_inode_operations; + inode->i_fop = &exfat_dir_operations; + set_nlink(inode, info->num_subdirs); + } else { /* regular file */ + inode->i_generation |= 1; + inode->i_mode = exfat_make_mode(sbi, info->attr, 0777); + inode->i_op = &exfat_file_inode_operations; + inode->i_fop = &exfat_file_operations; + inode->i_mapping->a_ops = &exfat_aops; + inode->i_mapping->nrpages = 0; + } + + i_size_write(inode, size); + + /* ondisk and aligned size should be aligned with block size */ + if (size & (inode->i_sb->s_blocksize - 1)) { + size |= (inode->i_sb->s_blocksize - 1); + size++; + } + + ei->i_size_aligned = size; + ei->i_size_ondisk = size; + + exfat_save_attr(inode, info->attr); + + inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) & + ~(sbi->cluster_size - 1)) >> inode->i_blkbits; + + exfat_time_fat2unix(sbi, &inode->i_mtime, &info->modify_timestamp); + exfat_time_fat2unix(sbi, &inode->i_ctime, &info->create_timestamp); + exfat_time_fat2unix(sbi, &inode->i_atime, &info->access_timestamp); + + exfat_cache_init_inode(inode); + + return 0; +} + +struct inode *exfat_build_inode(struct super_block *sb, + struct exfat_dir_entry *info, loff_t i_pos) +{ + struct inode *inode; + int err; + + inode = exfat_iget(sb, i_pos); + if (inode) + goto out; + inode = new_inode(sb); + if (!inode) { + inode = ERR_PTR(-ENOMEM); + goto out; + } + inode->i_ino = iunique(sb, EXFAT_ROOT_INO); + inode_set_iversion(inode, 1); + err = exfat_fill_inode(inode, info); + if (err) { + iput(inode); + inode = ERR_PTR(err); + goto out; + } + exfat_hash_inode(inode, i_pos); + insert_inode_hash(inode); +out: + return inode; +} + +void exfat_evict_inode(struct inode *inode) +{ + truncate_inode_pages(&inode->i_data, 0); + + if (!inode->i_nlink) { + i_size_write(inode, 0); + mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock); + __exfat_truncate(inode, 0); + mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock); + } + + invalidate_inode_buffers(inode); + clear_inode(inode); + exfat_cache_inval_inode(inode); + exfat_unhash_inode(inode); +} diff --git a/fs/exfat/namei.c b/fs/exfat/namei.c new file mode 100644 index 000000000000..2de92a6907e2 --- /dev/null +++ b/fs/exfat/namei.c @@ -0,0 +1,1492 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include + +#include "exfat_raw.h" +#include "exfat_fs.h" + +static inline unsigned long exfat_d_version(struct dentry *dentry) +{ + return (unsigned long) dentry->d_fsdata; +} + +static inline void exfat_d_version_set(struct dentry *dentry, + unsigned long version) +{ + dentry->d_fsdata = (void *) version; +} + +/* + * If new entry was created in the parent, it could create the 8.3 + * alias (the shortname of logname). So, the parent may have the + * negative-dentry which matches the created 8.3 alias. + * + * If it happened, the negative dentry isn't actually negative + * anymore. So, drop it. + */ +static int __exfat_revalidate_common(struct dentry *dentry) +{ + int ret = 1; + + spin_lock(&dentry->d_lock); + if (!inode_eq_iversion(d_inode(dentry->d_parent), + exfat_d_version(dentry))) + ret = 0; + spin_unlock(&dentry->d_lock); + return ret; +} + +static int __exfat_revalidate(struct dentry *dentry) +{ + /* This is not negative dentry. Always valid. */ + if (d_really_is_positive(dentry)) + return 1; + return __exfat_revalidate_common(dentry); +} + +static int __exfat_revalidate_ci(struct dentry *dentry, unsigned int flags) +{ + /* + * This is not negative dentry. Always valid. + * + * Note, rename() to existing directory entry will have ->d_inode, + * and will use existing name which isn't specified name by user. + * + * We may be able to drop this positive dentry here. But dropping + * positive dentry isn't good idea. So it's unsupported like + * rename("filename", "FILENAME") for now. + */ + if (d_really_is_positive(dentry)) + return 1; + /* + * Drop the negative dentry, in order to make sure to use the + * case sensitive name which is specified by user if this is + * for creation. + */ + if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) + return 0; + return __exfat_revalidate_common(dentry); +} + + +/* returns the length of a struct qstr, ignoring trailing dots */ +static unsigned int __exfat_striptail_len(unsigned int len, const char *name) +{ + while (len && name[len - 1] == '.') + len--; + return len; +} + +static unsigned int exfat_striptail_len(const struct qstr *qstr) +{ + return __exfat_striptail_len(qstr->len, qstr->name); +} + +static inline unsigned int __exfat_full_name_hash(const struct dentry *dentry, + const char *name, unsigned int len) +{ + return full_name_hash(dentry, name, len); +} + +static inline unsigned long __exfat_init_name_hash(const struct dentry *dentry) +{ + return init_name_hash(dentry); +} + +/* + * Compute the hash for the exfat name corresponding to the dentry. + * Note: if the name is invalid, we leave the hash code unchanged so + * that the existing dentry can be used. The exfat fs routines will + * return ENOENT or EINVAL as appropriate. + */ +static int exfat_d_hash(const struct dentry *dentry, struct qstr *qstr) +{ + unsigned int len = exfat_striptail_len(qstr); + + qstr->hash = __exfat_full_name_hash(dentry, qstr->name, len); + return 0; +} + +/* + * Compute the hash for the exfat name corresponding to the dentry. + * Note: if the name is invalid, we leave the hash code unchanged so + * that the existing dentry can be used. The exfat fs routines will + * return ENOENT or EINVAL as appropriate. + */ +static int exfat_d_hashi(const struct dentry *dentry, struct qstr *qstr) +{ + struct nls_table *t = EXFAT_SB(dentry->d_sb)->nls_io; + const unsigned char *name; + unsigned int len; + unsigned long hash; + + name = qstr->name; + len = exfat_striptail_len(qstr); + + hash = __exfat_init_name_hash(dentry); + while (len--) + hash = partial_name_hash(nls_tolower(t, *name++), hash); + qstr->hash = end_name_hash(hash); + + return 0; +} + +/* + * Case sensitive compare of two exfat names. + */ +static int exfat_cmp(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name) +{ + unsigned int alen, blen; + + /* A filename cannot end in '.' or we treat it like it has none */ + alen = exfat_striptail_len(name); + blen = __exfat_striptail_len(len, str); + if (alen == blen) { + if (strncmp(name->name, str, alen) == 0) + return 0; + } + return 1; +} + +/* + * Case insensitive compare of two exfat names. + */ +static int exfat_cmpi(const struct dentry *dentry, unsigned int len, + const char *str, const struct qstr *name) +{ + struct nls_table *t = EXFAT_SB(dentry->d_sb)->nls_io; + unsigned int alen, blen; + + /* A filename cannot end in '.' or we treat it like it has none */ + alen = exfat_striptail_len(name); + blen = __exfat_striptail_len(len, str); + if (alen == blen) { + if (nls_strnicmp(t, name->name, str, alen) == 0) + return 0; + } + return 1; +} + +static int exfat_revalidate(struct dentry *dentry, unsigned int flags) +{ + if (flags & LOOKUP_RCU) + return -ECHILD; + + return __exfat_revalidate(dentry); +} + +static int exfat_revalidate_ci(struct dentry *dentry, unsigned int flags) +{ + if (flags & LOOKUP_RCU) + return -ECHILD; + + return __exfat_revalidate_ci(dentry, flags); +} + +const struct dentry_operations exfat_dentry_ops = { + .d_revalidate = exfat_revalidate, + .d_hash = exfat_d_hash, + .d_compare = exfat_cmp, +}; + +const struct dentry_operations exfat_ci_dentry_ops = { + .d_revalidate = exfat_revalidate_ci, + .d_hash = exfat_d_hashi, + .d_compare = exfat_cmpi, +}; + +/* used only in search empty_slot() */ +#define CNT_UNUSED_NOHIT (-1) +#define CNT_UNUSED_HIT (-2) +/* search EMPTY CONTINUOUS "num_entries" entries */ +static int exfat_search_empty_slot(struct super_block *sb, + struct exfat_hint_femp *hint_femp, struct exfat_chain *p_dir, + int num_entries) +{ + int i, dentry, num_empty = 0, ret = -ENOSPC; + int dentries_per_clu; + unsigned int type; + struct exfat_chain *clu; + struct exfat_dentry *ep; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bh; + + dentries_per_clu = sbi->dentries_per_clu; + + if (hint_femp->eidx != EXFAT_HINT_NONE) { + dentry = hint_femp->eidx; + if (num_entries <= hint_femp->count) { + hint_femp->eidx = EXFAT_HINT_NONE; + return dentry; + } + + clu = exfat_chain_dup(&hint_femp->cur); + } else { + clu = exfat_chain_dup(p_dir); + dentry = 0; + } + + while (clu->dir != EOF_CLUSTER) { + i = dentry & (dentries_per_clu - 1); + + for (; i < dentries_per_clu; i++, dentry++) { + ep = exfat_get_dentry(sb, clu, i, &bh, NULL); + if (!ep) { + ret = -EIO; + goto free_clu; + } + type = exfat_get_entry_type(ep); + brelse(bh); + + if (type == TYPE_UNUSED || type == TYPE_DELETED) { + num_empty++; + if (hint_femp->eidx == EXFAT_HINT_NONE) { + hint_femp->eidx = dentry; + hint_femp->count = CNT_UNUSED_NOHIT; + exfat_chain_set(&hint_femp->cur, + clu->dir, clu->size, + clu->flags); + } + + if (type == TYPE_UNUSED && + hint_femp->count != CNT_UNUSED_HIT) + hint_femp->count = CNT_UNUSED_HIT; + } else { + if (hint_femp->eidx != EXFAT_HINT_NONE && + hint_femp->count == CNT_UNUSED_HIT) { + /* unused empty group means + * an empty group which includes + * unused dentry + */ + exfat_fs_error(sb, + "found bogus dentry(%d) beyond unused empty group(%d) (start_clu : %u, cur_clu : %u)", + dentry, hint_femp->eidx, + p_dir->dir, clu->dir); + ret = -EIO; + goto free_clu; + } + + num_empty = 0; + hint_femp->eidx = EXFAT_HINT_NONE; + } + + if (num_empty >= num_entries) { + /* found and invalidate hint_femp */ + hint_femp->eidx = EXFAT_HINT_NONE; + ret = (dentry - (num_entries - 1)); + goto free_clu; + } + } + + if (clu->flags == 0x03) { + if (--clu->size > 0) + clu->dir++; + else + clu->dir = EOF_CLUSTER; + } else { + if (exfat_get_next_cluster(sb, &clu->dir)) { + ret = -EIO; + goto free_clu; + } + } + } +free_clu: + kfree(clu); + return ret; +} + +static int exfat_check_max_dentries(struct inode *inode) +{ + if (EXFAT_B_TO_DEN(i_size_read(inode)) >= MAX_EXFAT_DENTRIES) { + /* + * exFAT spec allows a dir to grow upto 8388608(256MB) + * dentries + */ + return -ENOSPC; + } + return 0; +} + +/* find empty directory entry. + * if there isn't any empty slot, expand cluster chain. + */ +int exfat_find_empty_entry(struct inode *inode, struct exfat_chain *p_dir, + int num_entries) +{ + int dentry; + unsigned int ret, last_clu; + sector_t sector; + loff_t size = 0; + struct exfat_chain clu; + struct exfat_dentry *ep = NULL; + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *ei = EXFAT_I(inode); + struct exfat_hint_femp hint_femp; + + hint_femp.eidx = EXFAT_HINT_NONE; + + if (ei->hint_femp.eidx != EXFAT_HINT_NONE) { + memcpy(&hint_femp, &ei->hint_femp, + sizeof(struct exfat_hint_femp)); + ei->hint_femp.eidx = EXFAT_HINT_NONE; + } + + while ((dentry = exfat_search_empty_slot(sb, &hint_femp, p_dir, + num_entries)) < 0) { + if (dentry == -EIO) + break; + + if (exfat_check_max_dentries(inode)) + return -ENOSPC; + + /* we trust p_dir->size regardless of FAT type */ + if (exfat_find_last_cluster(sb, p_dir, &last_clu)) + return -EIO; + + /* + * Allocate new cluster to this directory + */ + exfat_chain_set(&clu, last_clu + 1, 0, p_dir->flags); + + /* allocate a cluster */ + ret = exfat_alloc_cluster(inode, 1, &clu); + if (ret) + return ret; + + if (exfat_zeroed_cluster(inode, clu.dir)) + return -EIO; + + /* append to the FAT chain */ + if (clu.flags != p_dir->flags) { + /* no-fat-chain bit is disabled, + * so fat-chain should be synced with alloc-bitmap + */ + exfat_chain_cont_cluster(sb, p_dir->dir, p_dir->size); + p_dir->flags = 0x01; + hint_femp.cur.flags = 0x01; + } + + if (clu.flags == 0x01) + if (exfat_ent_set(sb, last_clu, clu.dir)) + return -EIO; + + if (hint_femp.eidx == EXFAT_HINT_NONE) { + /* the special case that new dentry + * should be allocated from the start of new cluster + */ + hint_femp.eidx = EXFAT_B_TO_DEN_IDX(p_dir->size, sbi); + hint_femp.count = sbi->dentries_per_clu; + + exfat_chain_set(&hint_femp.cur, clu.dir, 0, clu.flags); + } + hint_femp.cur.size++; + p_dir->size++; + size = EXFAT_CLU_TO_B(p_dir->size, sbi); + + /* update the directory entry */ + if (p_dir->dir != sbi->root_dir) { + struct buffer_head *bh; + + ep = exfat_get_dentry(sb, + &(ei->dir), ei->entry + 1, &bh, §or); + if (!ep) + return -EIO; + + ep->stream_valid_size = cpu_to_le64(size); + ep->stream_size = ep->stream_valid_size; + ep->stream_flags = p_dir->flags; + exfat_update_bh(sb, bh, IS_DIRSYNC(inode)); + brelse(bh); + if (update_dir_chksum(inode, &(ei->dir), ei->entry)) + return -EIO; + } + + /* directory inode should be updated in here */ + i_size_write(inode, size); + EXFAT_I(inode)->i_size_ondisk += sbi->cluster_size; + EXFAT_I(inode)->i_size_aligned += sbi->cluster_size; + EXFAT_I(inode)->flags = p_dir->flags; + inode->i_blocks += 1 << sbi->sect_per_clus_bits; + } + + return dentry; +} + +/* + * Name Resolution Functions : + * Zero if it was successful; otherwise nonzero. + */ +static int __exfat_resolve_path(struct inode *inode, const unsigned char *path, + struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname, + int lookup) +{ + int namelen; + int lossy = NLS_NAME_NO_LOSSY; + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *ei = EXFAT_I(inode); + + /* DOT and DOTDOT are handled by VFS layer */ + + /* strip all trailing spaces */ + /* DO NOTHING : Is needed? */ + + /* strip all trailing periods */ + namelen = __exfat_striptail_len(strlen(path), path); + if (!namelen) + return -ENOENT; + + /* the limitation of linux? */ + if (strlen(path) > (MAX_NAME_LENGTH * MAX_CHARSET_SIZE)) + return -ENAMETOOLONG; + + /* + * strip all leading spaces : + * "MS windows 7" supports leading spaces. + * So we should skip this preprocessing for compatibility. + */ + + /* file name conversion : + * If lookup case, we allow bad-name for compatibility. + */ + namelen = exfat_nls_vfsname_to_uni16s(sb, path, namelen, p_uniname, + &lossy); + if (namelen < 0) + return namelen; /* return error value */ + + if ((lossy && !lookup) || !namelen) + return -EINVAL; + + exfat_chain_set(p_dir, ei->start_clu, + EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); + + return 0; +} + +static inline int exfat_resolve_path(struct inode *inode, + const unsigned char *path, struct exfat_chain *dir, + struct exfat_uni_name *uni) +{ + return __exfat_resolve_path(inode, path, dir, uni, 0); +} + +static inline int exfat_resolve_path_for_lookup(struct inode *inode, + const unsigned char *path, struct exfat_chain *dir, + struct exfat_uni_name *uni) +{ + return __exfat_resolve_path(inode, path, dir, uni, 1); +} + +static inline loff_t exfat_make_i_pos(struct exfat_dir_entry *info) +{ + return ((loff_t) info->dir.dir << 32) | (info->entry & 0xffffffff); +} + +static int exfat_add_entry(struct inode *inode, const char *path, + struct exfat_chain *p_dir, unsigned int type, + struct exfat_dir_entry *info) +{ + int ret, dentry, num_entries; + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_uni_name uniname; + struct exfat_chain clu; + int clu_size = 0; + unsigned int start_clu = FREE_CLUSTER; + + ret = exfat_resolve_path(inode, path, p_dir, &uniname); + if (ret) + goto out; + + num_entries = exfat_get_num_entries(&uniname); + if (num_entries < 0) { + ret = num_entries; + goto out; + } + + /* exfat_find_empty_entry must be called before alloc_cluster() */ + dentry = exfat_find_empty_entry(inode, p_dir, num_entries); + if (dentry < 0) { + ret = dentry; /* -EIO or -ENOSPC */ + goto out; + } + + if (type == TYPE_DIR) { + ret = exfat_alloc_new_dir(inode, &clu); + if (ret) + goto out; + start_clu = clu.dir; + clu_size = sbi->cluster_size; + } + + /* update the directory entry */ + /* fill the dos name directory entry information of the created file. + * the first cluster is not determined yet. (0) + */ + ret = exfat_init_dir_entry(inode, p_dir, dentry, type, + start_clu, clu_size); + if (ret) + goto out; + + ret = exfat_init_ext_entry(inode, p_dir, dentry, num_entries, &uniname); + if (ret) + goto out; + + memcpy(&info->dir, p_dir, sizeof(struct exfat_chain)); + info->entry = dentry; + info->flags = 0x03; + info->type = type; + + if (type == TYPE_FILE) { + info->attr = ATTR_ARCHIVE; + info->start_clu = EOF_CLUSTER; + info->size = 0; + info->num_subdirs = 0; + } else { + int count; + struct exfat_chain cdir; + + info->attr = ATTR_SUBDIR; + info->start_clu = start_clu; + info->size = clu_size; + + exfat_chain_set(&cdir, info->start_clu, + EXFAT_B_TO_CLU(info->size, sbi), info->flags); + count = exfat_count_dir_entries(sb, &cdir); + if (count < 0) + return -EIO; + info->num_subdirs = count + EXFAT_MIN_SUBDIR; + } + memset(&info->create_timestamp, 0, + sizeof(struct exfat_date_time)); + memset(&info->modify_timestamp, 0, + sizeof(struct exfat_date_time)); + memset(&info->access_timestamp, 0, + sizeof(struct exfat_date_time)); +out: + return ret; +} + +static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode, + bool excl) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct exfat_chain cdir; + struct exfat_dir_entry info; + loff_t i_pos; + int err; + + mutex_lock(&EXFAT_SB(sb)->s_lock); + exfat_set_vol_flags(sb, VOL_DIRTY); + err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_FILE, + &info); + exfat_set_vol_flags(sb, VOL_CLEAN); + if (err) + goto unlock; + + inode_inc_iversion(dir); + dir->i_ctime = dir->i_mtime = dir->i_atime = current_time(dir); + if (IS_DIRSYNC(dir)) + exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + i_pos = exfat_make_i_pos(&info); + inode = exfat_build_inode(sb, &info, i_pos); + if (IS_ERR(inode)) + goto unlock; + + inode_inc_iversion(inode); + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + + d_instantiate(dentry, inode); +unlock: + mutex_unlock(&EXFAT_SB(sb)->s_lock); + return err; +} + +/* lookup a file */ +static int exfat_find(struct inode *dir, struct qstr *qname, + struct exfat_dir_entry *info) +{ + int ret, dentry, num_entries, count; + struct exfat_chain cdir; + struct exfat_uni_name uni_name; + struct exfat_dentry *ep, *ep2; + struct exfat_entry_set_cache *es = NULL; + struct super_block *sb = dir->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *ei = EXFAT_I(dir); + struct exfat_timestamp tm; + + if (qname->len == 0) + return -ENOENT; + + /* check the validity of directory name in the given pathname */ + ret = exfat_resolve_path_for_lookup(dir, qname->name, &cdir, &uni_name); + if (ret) + return ret; + + num_entries = exfat_get_num_entries(&uni_name); + if (num_entries < 0) + return num_entries; + + /* check the validation of hint_stat and initialize it if required */ + if (ei->version != (inode_peek_iversion_raw(dir) & 0xffffffff)) { + ei->hint_stat.clu = cdir.dir; + ei->hint_stat.eidx = 0; + ei->version = (inode_peek_iversion_raw(dir) & 0xffffffff); + ei->hint_femp.eidx = EXFAT_HINT_NONE; + } + + /* search the file name for directories */ + dentry = exfat_find_dir_entry(sb, ei, &cdir, &uni_name, + num_entries, TYPE_ALL); + + if ((dentry < 0) && (dentry != -EEXIST)) + return dentry; /* -error value */ + + memcpy(&info->dir, &cdir.dir, sizeof(struct exfat_chain)); + info->entry = dentry; + info->num_subdirs = 0; + + /* root directory itself */ + if (unlikely(dentry == -EEXIST)) { + int num_clu = 0; + + info->type = TYPE_DIR; + info->attr = ATTR_SUBDIR; + info->flags = 0x01; + info->start_clu = sbi->root_dir; + memset(&info->create_timestamp, 0, + sizeof(struct exfat_date_time)); + memset(&info->modify_timestamp, 0, + sizeof(struct exfat_date_time)); + memset(&info->access_timestamp, 0, + sizeof(struct exfat_date_time)); + + exfat_chain_set(&cdir, sbi->root_dir, 0, 0x01); + if (exfat_count_num_clusters(sb, &cdir, &num_clu)) + return -EIO; + info->size = num_clu << sbi->cluster_size_bits; + + count = exfat_count_dir_entries(sb, &cdir); + if (count < 0) + return -EIO; + + info->num_subdirs = count; + } else { + es = exfat_get_dentry_set(sb, &cdir, dentry, ES_2_ENTRIES, &ep); + if (!es) + return -EIO; + ep2 = ep + 1; + + info->type = exfat_get_entry_type(ep); + info->attr = le16_to_cpu(ep->file_attr); + info->size = le64_to_cpu(ep2->stream_valid_size); + if ((info->type == TYPE_FILE) && (info->size == 0)) { + info->flags = 0x03; + info->start_clu = EOF_CLUSTER; + } else { + info->flags = ep2->stream_flags; + info->start_clu = le32_to_cpu(ep2->stream_start_clu); + } + + if (ei->start_clu == FREE_CLUSTER) { + exfat_fs_error(sb, + "non-zero size file starts with zero cluster (size : %llu, p_dir : %u, entry : 0x%08x)", + i_size_read(dir), ei->dir.dir, ei->entry); + return -EIO; + } + + exfat_get_entry_time(ep, &tm, TM_CREATE); + info->create_timestamp.year = tm.year; + info->create_timestamp.month = tm.mon; + info->create_timestamp.day = tm.day; + info->create_timestamp.hour = tm.hour; + info->create_timestamp.minute = tm.min; + info->create_timestamp.second = tm.sec; + info->create_timestamp.milli_second = 0; + + exfat_get_entry_time(ep, &tm, TM_MODIFY); + info->modify_timestamp.year = tm.year; + info->modify_timestamp.month = tm.mon; + info->modify_timestamp.day = tm.day; + info->modify_timestamp.hour = tm.hour; + info->modify_timestamp.minute = tm.min; + info->modify_timestamp.second = tm.sec; + info->modify_timestamp.milli_second = 0; + + memset(&info->access_timestamp, 0, + sizeof(struct exfat_date_time)); + kfree(es); + + if (info->type == TYPE_DIR) { + exfat_chain_set(&cdir, info->start_clu, + EXFAT_B_TO_CLU(info->size, sbi), info->flags); + count = exfat_count_dir_entries(sb, &cdir); + if (count < 0) + return -EIO; + + info->num_subdirs = count + EXFAT_MIN_SUBDIR; + } + } + + return 0; +} + +static int exfat_d_anon_disconn(struct dentry *dentry) +{ + return IS_ROOT(dentry) && (dentry->d_flags & DCACHE_DISCONNECTED); +} + +static struct dentry *exfat_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct dentry *alias; + struct exfat_dir_entry info; + int err; + loff_t i_pos; + mode_t i_mode; + + mutex_lock(&EXFAT_SB(sb)->s_lock); + err = exfat_find(dir, &dentry->d_name, &info); + if (err) { + if (err == -ENOENT) { + inode = NULL; + goto out; + } + goto unlock; + } + + i_pos = exfat_make_i_pos(&info); + inode = exfat_build_inode(sb, &info, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto unlock; + } + + i_mode = inode->i_mode; + alias = d_find_alias(inode); + + /* + * Checking "alias->d_parent == dentry->d_parent" to make sure + * FS is not corrupted (especially double linked dir). + */ + if (alias && alias->d_parent == dentry->d_parent && + !exfat_d_anon_disconn(alias)) { + + /* + * Unhashed alias is able to exist because of revalidate() + * called by lookup_fast. You can easily make this status + * by calling create and lookup concurrently + * In such case, we reuse an alias instead of new dentry + */ + if (d_unhashed(alias)) { + WARN_ON(alias->d_name.hash_len != + dentry->d_name.hash_len); + exfat_msg(sb, KERN_INFO, + "rehashed a dentry(%p) in read lookup", alias); + d_drop(dentry); + d_rehash(alias); + } else if (!S_ISDIR(i_mode)) { + /* + * This inode has non anonymous-DCACHE_DISCONNECTED + * dentry. This means, the user did ->lookup() by an + * another name (longname vs 8.3 alias of it) in past. + * + * Switch to new one for reason of locality if possible. + */ + d_move(alias, dentry); + } + iput(inode); + mutex_unlock(&EXFAT_SB(sb)->s_lock); + return alias; + } + dput(alias); +out: + /* initialize d_time even though it is positive dentry */ + dentry->d_time = inode_peek_iversion_raw(dir); + mutex_unlock(&EXFAT_SB(sb)->s_lock); + if (!inode) + exfat_d_version_set(dentry, inode_query_iversion(dir)); + + return d_splice_alias(inode, dentry); +unlock: + mutex_unlock(&EXFAT_SB(sb)->s_lock); + return ERR_PTR(err); +} + +/* remove an entry, BUT don't truncate */ +static int exfat_unlink(struct inode *dir, struct dentry *dentry) +{ + struct exfat_chain *cdir; + struct exfat_dentry *ep; + struct super_block *sb = dir->i_sb; + struct inode *inode = dentry->d_inode; + struct exfat_inode_info *ei = EXFAT_I(inode); + struct buffer_head *bh; + sector_t sector; + int num_entries, entry, err = 0; + + mutex_lock(&EXFAT_SB(sb)->s_lock); + cdir = exfat_chain_dup(&ei->dir); + if (!cdir) { + err = -ENOMEM; + goto unlock; + } + + entry = ei->entry; + + if (ei->dir.dir == DIR_DELETED) { + exfat_msg(sb, KERN_ERR, "abnormal access to deleted dentry"); + err = -ENOENT; + goto unlock; + } + + ep = exfat_get_dentry(sb, cdir, entry, &bh, §or); + if (!ep) { + err = -EIO; + goto unlock; + } + num_entries = exfat_count_ext_entries(sb, cdir, entry, ep); + if (num_entries < 0) { + err = -EIO; + brelse(bh); + goto unlock; + } + num_entries++; + brelse(bh); + + exfat_set_vol_flags(sb, VOL_DIRTY); + /* update the directory entry */ + if (exfat_remove_entries(dir, cdir, entry, 0, num_entries)) { + err = -EIO; + goto unlock; + } + + /* This doesn't modify ei */ + ei->dir.dir = DIR_DELETED; + exfat_set_vol_flags(sb, VOL_CLEAN); + + inode_inc_iversion(dir); + dir->i_mtime = dir->i_atime = current_time(dir); + if (IS_DIRSYNC(dir)) + exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + + clear_nlink(inode); + inode->i_mtime = inode->i_atime = current_time(inode); + exfat_unhash_inode(inode); + dentry->d_time = inode_peek_iversion_raw(dir); + exfat_d_version_set(dentry, inode_query_iversion(dir)); +unlock: + mutex_unlock(&EXFAT_SB(sb)->s_lock); + kfree(cdir); + + return err; +} + +static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + struct super_block *sb = dir->i_sb; + struct inode *inode; + struct exfat_dir_entry info; + struct exfat_chain cdir; + loff_t i_pos; + int err; + + mutex_lock(&EXFAT_SB(sb)->s_lock); + exfat_set_vol_flags(sb, VOL_DIRTY); + err = exfat_add_entry(dir, dentry->d_name.name, &cdir, TYPE_DIR, + &info); + exfat_set_vol_flags(sb, VOL_CLEAN); + if (err) + goto unlock; + + inode_inc_iversion(dir); + dir->i_ctime = dir->i_mtime = dir->i_atime = current_time(dir); + if (IS_DIRSYNC(dir)) + exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + inc_nlink(dir); + + i_pos = exfat_make_i_pos(&info); + inode = exfat_build_inode(sb, &info, i_pos); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto unlock; + } + + inode_inc_iversion(inode); + inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); + /* timestamp is already written, so mark_inode_dirty() is unneeded. */ + + d_instantiate(dentry, inode); + +unlock: + mutex_unlock(&EXFAT_SB(sb)->s_lock); + return err; +} + +static int exfat_check_dir_empty(struct super_block *sb, + struct exfat_chain *p_dir) +{ + int i, ret = 0; + int dentries_per_clu; + unsigned int type; + struct exfat_chain *clu; + struct exfat_dentry *ep; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bh; + + dentries_per_clu = sbi->dentries_per_clu; + + clu = exfat_chain_dup(p_dir); + + while (clu->dir != EOF_CLUSTER) { + for (i = 0; i < dentries_per_clu; i++) { + ep = exfat_get_dentry(sb, clu, i, &bh, NULL); + if (!ep) { + ret = -EIO; + goto free_clu; + } + type = exfat_get_entry_type(ep); + brelse(bh); + if (type == TYPE_UNUSED) + goto free_clu; + + if (type != TYPE_FILE && type != TYPE_DIR) + continue; + + ret = -ENOTEMPTY; + goto free_clu; + } + + if (clu->flags == 0x03) { + if (--clu->size > 0) + clu->dir++; + else + clu->dir = EOF_CLUSTER; + } else { + if (exfat_get_next_cluster(sb, &(clu->dir))) { + ret = -EIO; + goto free_clu; + } + } + } + +free_clu: + kfree(clu); + return ret; +} + +static int exfat_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + struct exfat_dentry *ep; + struct exfat_chain *cdir, clu_to_free; + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *ei = EXFAT_I(inode); + struct buffer_head *bh; + sector_t sector; + int num_entries, entry, err; + + mutex_lock(&EXFAT_SB(inode->i_sb)->s_lock); + + cdir = exfat_chain_dup(&ei->dir); + entry = ei->entry; + + if (ei->dir.dir == DIR_DELETED) { + exfat_msg(sb, KERN_ERR, "abnormal access to deleted dentry"); + err = -ENOENT; + goto unlock; + } + + exfat_set_vol_flags(sb, VOL_DIRTY); + exfat_chain_set(&clu_to_free, ei->start_clu, + EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi), ei->flags); + + err = exfat_check_dir_empty(sb, &clu_to_free); + if (err) { + if (err == -EIO) + exfat_msg(sb, KERN_ERR, + "failed to exfat_check_dir_empty : err(%d)", + err); + goto unlock; + } + + ep = exfat_get_dentry(sb, cdir, entry, &bh, §or); + if (!ep) { + err = -EIO; + goto unlock; + } + + num_entries = exfat_count_ext_entries(sb, cdir, entry, ep); + if (num_entries < 0) { + err = -EIO; + brelse(bh); + goto unlock; + } + num_entries++; + brelse(bh); + + err = exfat_remove_entries(dir, cdir, entry, 0, num_entries); + if (err) { + exfat_msg(sb, KERN_ERR, + "failed to exfat_remove_entries : err(%d)", + err); + goto unlock; + } + ei->dir.dir = DIR_DELETED; + exfat_set_vol_flags(sb, VOL_CLEAN); + + inode_inc_iversion(dir); + dir->i_mtime = dir->i_atime = current_time(dir); + if (IS_DIRSYNC(dir)) + exfat_sync_inode(dir); + else + mark_inode_dirty(dir); + drop_nlink(dir); + + clear_nlink(inode); + inode->i_mtime = inode->i_atime = current_time(inode); + exfat_unhash_inode(inode); + dentry->d_time = inode_peek_iversion_raw(dir); + exfat_d_version_set(dentry, inode_query_iversion(dir)); +unlock: + mutex_unlock(&EXFAT_SB(inode->i_sb)->s_lock); + kfree(cdir); + return err; +} + +static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir, + int oldentry, struct exfat_uni_name *p_uniname, + struct exfat_inode_info *ei) +{ + int ret, num_old_entries, num_new_entries; + sector_t sector_old, sector_new; + struct exfat_dentry *epold, *epnew; + struct super_block *sb = inode->i_sb; + struct buffer_head *new_bh, *old_bh; + int sync = IS_DIRSYNC(inode); + + epold = exfat_get_dentry(sb, p_dir, oldentry, &old_bh, §or_old); + if (!epold) + return -EIO; + + num_old_entries = exfat_count_ext_entries(sb, p_dir, oldentry, epold); + if (num_old_entries < 0) + return -EIO; + num_old_entries++; + + num_new_entries = exfat_get_num_entries(p_uniname); + if (num_new_entries < 0) + return num_new_entries; + + if (num_old_entries < num_new_entries) { + int newentry; + + newentry = + exfat_find_empty_entry(inode, p_dir, num_new_entries); + if (newentry < 0) + return newentry; /* -EIO or -ENOSPC */ + + epnew = exfat_get_dentry(sb, p_dir, newentry, &new_bh, + §or_new); + if (!epnew) + return -EIO; + + memcpy(epnew, epold, DENTRY_SIZE); + if (exfat_get_entry_type(epnew) == TYPE_FILE) { + epnew->file_attr |= ATTR_ARCHIVE_LE; + ei->attr |= ATTR_ARCHIVE; + } + exfat_update_bh(sb, new_bh, sync); + brelse(old_bh); + brelse(new_bh); + + epold = exfat_get_dentry(sb, p_dir, oldentry + 1, &old_bh, + §or_old); + epnew = exfat_get_dentry(sb, p_dir, newentry + 1, &new_bh, + §or_new); + if (!epold || !epnew) + return -EIO; + + memcpy(epnew, epold, DENTRY_SIZE); + exfat_update_bh(sb, new_bh, sync); + brelse(old_bh); + brelse(new_bh); + + ret = exfat_init_ext_entry(inode, p_dir, newentry, + num_new_entries, p_uniname); + if (ret) + return ret; + + exfat_remove_entries(inode, p_dir, oldentry, 0, + num_old_entries); + ei->entry = newentry; + } else { + if (exfat_get_entry_type(epold) == TYPE_FILE) { + epold->file_attr |= ATTR_ARCHIVE_LE; + ei->attr |= ATTR_ARCHIVE; + } + exfat_update_bh(sb, old_bh, sync); + brelse(old_bh); + ret = exfat_init_ext_entry(inode, p_dir, oldentry, + num_new_entries, p_uniname); + if (ret) + return ret; + + exfat_remove_entries(inode, p_dir, oldentry, num_new_entries, + num_old_entries); + } + + return 0; +} + +static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir, + int oldentry, struct exfat_chain *p_newdir, + struct exfat_uni_name *p_uniname, struct exfat_inode_info *ei) +{ + int ret, newentry, num_new_entries, num_old_entries; + sector_t sector_mov, sector_new; + struct exfat_dentry *epmov, *epnew; + struct super_block *sb = inode->i_sb; + struct buffer_head *mov_bh, *new_bh; + + epmov = exfat_get_dentry(sb, p_olddir, oldentry, &mov_bh, §or_mov); + if (!epmov) + return -EIO; + + /* check if the source and target directory is the same */ + if (exfat_get_entry_type(epmov) == TYPE_DIR && + le32_to_cpu(epmov->stream_start_clu) == p_newdir->dir) + return -EINVAL; + + num_old_entries = exfat_count_ext_entries(sb, p_olddir, oldentry, + epmov); + if (num_old_entries < 0) + return -EIO; + num_old_entries++; + + num_new_entries = exfat_get_num_entries(p_uniname); + if (num_new_entries < 0) + return num_new_entries; + + newentry = exfat_find_empty_entry(inode, p_newdir, num_new_entries); + if (newentry < 0) + return newentry; /* -EIO or -ENOSPC */ + + epnew = exfat_get_dentry(sb, p_newdir, newentry, &new_bh, §or_new); + if (!epnew) + return -EIO; + + memcpy(epnew, epmov, DENTRY_SIZE); + if (exfat_get_entry_type(epnew) == TYPE_FILE) { + epnew->file_attr |= ATTR_ARCHIVE_LE; + ei->attr |= ATTR_ARCHIVE; + } + exfat_update_bh(sb, new_bh, IS_DIRSYNC(inode)); + brelse(mov_bh); + brelse(new_bh); + + epmov = exfat_get_dentry(sb, p_olddir, oldentry + 1, &mov_bh, + §or_mov); + epnew = exfat_get_dentry(sb, p_newdir, newentry + 1, &new_bh, + §or_new); + if (!epmov || !epnew) + return -EIO; + + memcpy(epnew, epmov, DENTRY_SIZE); + exfat_update_bh(sb, new_bh, IS_DIRSYNC(inode)); + brelse(mov_bh); + brelse(new_bh); + + ret = exfat_init_ext_entry(inode, p_newdir, newentry, num_new_entries, + p_uniname); + if (ret) + return ret; + + exfat_remove_entries(inode, p_olddir, oldentry, 0, num_old_entries); + + exfat_chain_set(&ei->dir, p_newdir->dir, p_newdir->size, + p_newdir->flags); + + ei->entry = newentry; + + return 0; +} + +static void exfat_update_parent_info(struct exfat_inode_info *ei, + struct inode *parent_inode) +{ + struct exfat_sb_info *sbi = EXFAT_SB(parent_inode->i_sb); + struct exfat_inode_info *parent_ei = EXFAT_I(parent_inode); + loff_t parent_isize = i_size_read(parent_inode); + + /* + * the problem that struct exfat_inode_info caches wrong parent info. + * + * because of flag-mismatch of ei->dir, + * there is abnormal traversing cluster chain. + */ + if (unlikely(parent_ei->flags != ei->dir.flags || + parent_isize != EXFAT_CLU_TO_B(ei->dir.size, sbi) || + parent_ei->start_clu != ei->dir.dir)) { + exfat_chain_set(&ei->dir, parent_ei->start_clu, + EXFAT_B_TO_CLU_ROUND_UP(parent_isize, sbi), + parent_ei->flags); + } +} + +/* rename or move a old file into a new file */ +static int __exfat_rename(struct inode *old_parent_inode, + struct exfat_inode_info *ei, struct inode *new_parent_inode, + struct dentry *new_dentry) +{ + int ret; + int dentry; + struct exfat_chain *olddir, newdir; + struct exfat_chain *p_dir = NULL; + struct exfat_uni_name uni_name; + struct exfat_dentry *ep; + struct super_block *sb = old_parent_inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + const unsigned char *new_path = new_dentry->d_name.name; + struct inode *new_inode = new_dentry->d_inode; + int num_entries; + struct exfat_inode_info *new_ei = NULL; + unsigned int new_entry_type = TYPE_UNUSED; + int new_entry = 0; + struct buffer_head *old_bh, *new_bh = NULL; + + /* check the validity of pointer parameters */ + if (new_path == NULL || strlen(new_path) == 0) + return -EINVAL; + + if (ei->dir.dir == DIR_DELETED) { + exfat_msg(sb, KERN_ERR, + "abnormal access to deleted source dentry"); + return -ENOENT; + } + + exfat_update_parent_info(ei, old_parent_inode); + + olddir = exfat_chain_dup(&ei->dir); + dentry = ei->entry; + + ep = exfat_get_dentry(sb, olddir, dentry, &old_bh, NULL); + if (!ep) { + ret = -EIO; + goto out; + } + brelse(old_bh); + + /* check whether new dir is existing directory and empty */ + if (new_inode) { + ret = -EIO; + new_ei = EXFAT_I(new_inode); + + if (new_ei->dir.dir == DIR_DELETED) { + exfat_msg(sb, KERN_ERR, + "abnormal access to deleted target dentry"); + goto out; + } + + exfat_update_parent_info(new_ei, new_parent_inode); + + p_dir = &(new_ei->dir); + new_entry = new_ei->entry; + ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh, NULL); + if (!ep) + goto out; + + new_entry_type = exfat_get_entry_type(ep); + brelse(new_bh); + + /* if new_inode exists, update ei */ + if (new_entry_type == TYPE_DIR) { + struct exfat_chain new_clu; + + new_clu.dir = new_ei->start_clu; + new_clu.size = + EXFAT_B_TO_CLU_ROUND_UP(i_size_read(new_inode), + sbi); + new_clu.flags = new_ei->flags; + + ret = exfat_check_dir_empty(sb, &new_clu); + if (ret) + goto out; + } + } + + /* check the validity of directory name in the given new pathname */ + ret = exfat_resolve_path(new_parent_inode, new_path, &newdir, + &uni_name); + if (ret) + goto out; + + exfat_set_vol_flags(sb, VOL_DIRTY); + + if (olddir->dir == newdir.dir) + ret = exfat_rename_file(new_parent_inode, olddir, dentry, + &uni_name, ei); + else + ret = exfat_move_file(new_parent_inode, olddir, dentry, + &newdir, &uni_name, ei); + + if (!ret && new_inode) { + /* delete entries of new_dir */ + ep = exfat_get_dentry(sb, p_dir, new_entry, &new_bh, NULL); + if (!ep) { + ret = -EIO; + goto del_out; + } + + num_entries = exfat_count_ext_entries(sb, p_dir, new_entry, ep); + if (num_entries < 0) { + ret = -EIO; + goto del_out; + } + brelse(new_bh); + + if (exfat_remove_entries(new_inode, p_dir, new_entry, 0, + num_entries + 1)) { + ret = -EIO; + goto del_out; + } + + /* Free the clusters if new_inode is a dir(as if exfat_rmdir) */ + if (new_entry_type == TYPE_DIR) { + /* new_ei, new_clu_to_free */ + struct exfat_chain new_clu_to_free; + + exfat_chain_set(&new_clu_to_free, new_ei->start_clu, + EXFAT_B_TO_CLU_ROUND_UP(i_size_read(new_inode), + sbi), new_ei->flags); + + if (exfat_free_cluster(new_inode, &new_clu_to_free)) { + /* just set I/O error only */ + ret = -EIO; + } + + i_size_write(new_inode, 0); + new_ei->start_clu = EOF_CLUSTER; + new_ei->flags = 0x03; + } +del_out: + /* Update new_inode ei + * Prevent syncing removed new_inode + * (new_ei is already initialized above code ("if (new_inode)") + */ + new_ei->dir.dir = DIR_DELETED; + } + exfat_set_vol_flags(sb, VOL_CLEAN); +out: + kfree(olddir); + return ret; +} + +static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry, + unsigned int flags) +{ + struct inode *old_inode, *new_inode; + struct super_block *sb = old_dir->i_sb; + loff_t i_pos; + int err; + + /* + * The VFS already checks for existence, so for local filesystems + * the RENAME_NOREPLACE implementation is equivalent to plain rename. + * Don't support any other flags + */ + if (flags & ~RENAME_NOREPLACE) + return -EINVAL; + + mutex_lock(&EXFAT_SB(sb)->s_lock); + old_inode = old_dentry->d_inode; + new_inode = new_dentry->d_inode; + + err = __exfat_rename(old_dir, EXFAT_I(old_inode), new_dir, new_dentry); + if (err) + goto unlock; + + inode_inc_iversion(new_dir); + new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime = + current_time(new_dir); + if (IS_DIRSYNC(new_dir)) + exfat_sync_inode(new_dir); + else + mark_inode_dirty(new_dir); + + i_pos = ((loff_t)EXFAT_I(old_inode)->dir.dir << 32) | + (EXFAT_I(old_inode)->entry & 0xffffffff); + exfat_unhash_inode(old_inode); + exfat_hash_inode(old_inode, i_pos); + if (IS_DIRSYNC(new_dir)) + exfat_sync_inode(old_inode); + else + mark_inode_dirty(old_inode); + + if (S_ISDIR(old_inode->i_mode) && old_dir != new_dir) { + drop_nlink(old_dir); + if (!new_inode) + inc_nlink(new_dir); + } + + inode_inc_iversion(old_dir); + old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir); + if (IS_DIRSYNC(old_dir)) + exfat_sync_inode(old_dir); + else + mark_inode_dirty(old_dir); + + if (new_inode) { + exfat_unhash_inode(new_inode); + + /* skip drop_nlink if new_inode already has been dropped */ + if (new_inode->i_nlink) { + drop_nlink(new_inode); + if (S_ISDIR(new_inode->i_mode)) + drop_nlink(new_inode); + } else { + exfat_msg(sb, KERN_WARNING, + "abnormal access to an inode dropped"); + WARN_ON(new_inode->i_nlink == 0); + } + new_inode->i_ctime = current_time(new_inode); + } + +unlock: + mutex_unlock(&EXFAT_SB(sb)->s_lock); + return err; +} + +const struct inode_operations exfat_dir_inode_operations = { + .create = exfat_create, + .lookup = exfat_lookup, + .unlink = exfat_unlink, + .mkdir = exfat_mkdir, + .rmdir = exfat_rmdir, + .rename = exfat_rename, + .setattr = exfat_setattr, + .getattr = exfat_getattr, +}; From patchwork Tue Nov 19 09:37:09 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11251635 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2993D14C0 for ; Tue, 19 Nov 2019 09:41:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D64A322310 for ; Tue, 19 Nov 2019 09:41:13 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="TY7Xq6N5" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727761AbfKSJlJ (ORCPT ); Tue, 19 Nov 2019 04:41:09 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:14047 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727445AbfKSJk3 (ORCPT ); Tue, 19 Nov 2019 04:40:29 -0500 Received: from epcas1p1.samsung.com (unknown [182.195.41.45]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20191119094024epoutp0449c773970c1279bfd432f13fdd9d8341~Yhu2RAMlK0888008880epoutp04Y for ; Tue, 19 Nov 2019 09:40:24 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20191119094024epoutp0449c773970c1279bfd432f13fdd9d8341~Yhu2RAMlK0888008880epoutp04Y DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574156424; bh=smI7kwqIbE9lWeY5Ol2C6KxaA85uIMSUmsXYwlTv1Tg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=TY7Xq6N5VHNwSqfgJ9hyxcsOZl7M9Jjz2WAFl1gZsoCZc1v/Wmlv9q2Q2ZBAOL3mu JycSYFU9uwgtM1Ey/0gO2oYViHOub7L4ezBxv9jhv/GI3aVyWBZ/69DntqUc8Hb1vy szvi4w7bLg6z/sKC5e3rmfc66FsSwjqS7wzha9eU= Received: from epsnrtp2.localdomain (unknown [182.195.42.163]) by epcas1p4.samsung.com (KnoxPortal) with ESMTP id 20191119094024epcas1p492c43972073b25177929f7c34c972667~Yhu2ASBal0057100571epcas1p4J; Tue, 19 Nov 2019 09:40:24 +0000 (GMT) Received: from epsmges1p5.samsung.com (unknown [182.195.40.161]) by epsnrtp2.localdomain (Postfix) with ESMTP id 47HLQv07x6zMqYkb; Tue, 19 Nov 2019 09:40:23 +0000 (GMT) Received: from epcas1p1.samsung.com ( [182.195.41.45]) by epsmges1p5.samsung.com (Symantec Messaging Gateway) with SMTP id 7D.BF.04237.688B3DD5; Tue, 19 Nov 2019 18:40:22 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p2.samsung.com (KnoxPortal) with ESMTPA id 20191119094022epcas1p28a2fbdc256ad6b0590d0765194ccb6a8~Yhu0NOJPp0990309903epcas1p2N; Tue, 19 Nov 2019 09:40:22 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119094022epsmtrp17b43d1cdd8508add3258b4a937c633c5~Yhu0MjSJI0080100801epsmtrp1m; Tue, 19 Nov 2019 09:40:22 +0000 (GMT) X-AuditID: b6c32a39-913ff7000000108d-af-5dd3b8861a5a Received: from epsmtip2.samsung.com ( [182.195.34.31]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id C2.12.03654.688B3DD5; Tue, 19 Nov 2019 18:40:22 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20191119094022epsmtip2890e9fd2f2d1f0ec0d0a741c0d5fb5c5~Yhu0AhtWL0820508205epsmtip2B; Tue, 19 Nov 2019 09:40:22 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, Namjae Jeon Subject: [PATCH v3 04/13] exfat: add directory operations Date: Tue, 19 Nov 2019 04:37:09 -0500 Message-Id: <20191119093718.3501-5-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119093718.3501-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFmplk+LIzCtJLcpLzFFi42LZdlhTV7dtx+VYgw1LdS0OP57EbtG8eD2b xcrVR5ksrt+9xWyxZ+9JFovLu+awWfyf9ZzV4sf0eost/46wWlx6/4HFgctj56y77B77565h 99h9s4HNo2/LKkaPzaerPT5vkvM4tP0Nm8ftZ9tYAjiicmwyUhNTUosUUvOS81My89JtlbyD 453jTc0MDHUNLS3MlRTyEnNTbZVcfAJ03TJzgE5UUihLzCkFCgUkFhcr6dvZFOWXlqQqZOQX l9gqpRak5BQYGhToFSfmFpfmpesl5+daGRoYGJkCVSbkZDR3tbIUXO9iqtgztZO9gfHTecYu Rk4OCQETia1b9wLZXBxCAjsYJSaduM4C4XxilHj18ixYlZDAN0aJ9p9mMB27jzazQxTtZZTY vn4NK1zHhynHgTIcHGwC2hJ/toiCNIgI2Etsnn0AbCqzwBFGiRdfJrCDJIQFLCW+bFzMAmKz CKhKPHnzmwnE5hWwlrizsokdYpu8xOoNB5hBbE4BG4mHs5eB3SohcIJNou/PfxaQZRICLhIP f2pC1AtLvDq+BapXSuJlfxs7REm1xMf9zBDhDqAbvttC2MYSN9dvYAUpYRbQlFi/Sx8irCix 8/dcsN+ZBfgk3n3tYYWYwivR0SYEUaIq0XfpMBOELS3R1f4BapGHxOt/CpAA6WeUuP33IusE RrlZCAsWMDKuYhRLLSjOTU8tNiwwRY6wTYzgJKhluYPx2DmfQ4wCHIxKPLwK6pdjhVgTy4or cw8xSnAwK4nw+j26ECvEm5JYWZValB9fVJqTWnyI0RQYjBOZpUST84EJOq8k3tDUyNjY2MLE zNzM1FhJnJfjx8VYIYH0xJLU7NTUgtQimD4mDk6pBsZVRlNPMllNYVq1Lyrt657UuS+LtsS4 vcni5p/WzC5+zcyz9DfDaT7PQzM27OhmaMyIeLkmQPv7zqXa8qJuH1hZrq/at0vcfeOm1mO6 qdH3F6z/phuzZ9sKoZTOZ8G++0OmrHFffc9G7J+Vyn51h/vfAvhS/sywyDcQ+HPpz0kjCdUt 5VMTes8qsRRnJBpqMRcVJwIA3lOkHpgDAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrILMWRmVeSWpSXmKPExsWy7bCSvG7bjsuxBg/2iVkcfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9RZb/h1htbj0/gOLA5fHzll32T32z13D 7rH7ZgObR9+WVYwem09Xe3zeJOdxaPsbNo/bz7axBHBEcdmkpOZklqUW6dslcGU0d7WyFFzv YqrYM7WTvYHx03nGLkZODgkBE4ndR5vZuxi5OIQEdjNKbP+xiBkiIS1x7MQZIJsDyBaWOHy4 GKLmA6PE+6m9LCBxNgFtiT9bREHKRQQcJXp3HWYBqWEWOMcosfPZMrAFwgKWEl82LmYBsVkE VCWevPnNBGLzClhL3FnZxA6xS15i9YYDYHs5BWwkHs6G6BUCqml81Mw+gZFvASPDKkbJ1ILi 3PTcYsMCw7zUcr3ixNzi0rx0veT83E2M4IDV0tzBeHlJ/CFGAQ5GJR7eEyqXY4VYE8uKK3MP MUpwMCuJ8Po9uhArxJuSWFmVWpQfX1Sak1p8iFGag0VJnPdp3rFIIYH0xJLU7NTUgtQimCwT B6dUA2OG3F/LI+dqV8y32dG13c1iqVvfjOO2RoeOf1wryf12n/ib4txrn60Pcxg0Wl9kOrFe 4OTDAI/Nnt35MqndAg49XadFXPm2qWndcn0QYnFPMeKX6CM9pil1paZlLt/mTMw8ebffzH76 C61/Nn/PeDZlRHmdSAntWG0Unr+A+ePxqOy8G2zKk5VYijMSDbWYi4oTAbpTFj1UAgAA X-CMS-MailID: 20191119094022epcas1p28a2fbdc256ad6b0590d0765194ccb6a8 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119094022epcas1p28a2fbdc256ad6b0590d0765194ccb6a8 References: <20191119093718.3501-1-namjae.jeon@samsung.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This adds the implementation of directory operations for exfat. Signed-off-by: Namjae Jeon Signed-off-by: Sungjong Seo --- fs/exfat/dir.c | 1338 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1338 insertions(+) create mode 100644 fs/exfat/dir.c diff --git a/fs/exfat/dir.c b/fs/exfat/dir.c new file mode 100644 index 000000000000..2e33c4cf4f7f --- /dev/null +++ b/fs/exfat/dir.c @@ -0,0 +1,1338 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "exfat_raw.h" +#include "exfat_fs.h" + +/* read a directory entry from the opened directory */ +static int exfat_readdir(struct inode *inode, struct exfat_dir_entry *dir_entry) +{ + int i, ret = 0; + int dentries_per_clu, dentries_per_clu_bits = 0; + unsigned int type, clu_offset; + sector_t sector; + struct exfat_chain dir, *clu; + struct exfat_uni_name uni_name; + struct exfat_timestamp tm; + struct exfat_dentry *ep; + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *ei = EXFAT_I(inode); + unsigned int dentry = ei->rwoffset & 0xFFFFFFFF; + struct buffer_head *bh; + + /* check if the given file ID is opened */ + if (ei->type != TYPE_DIR) + return -EPERM; + + if (ei->entry == -1) + exfat_chain_set(&dir, sbi->root_dir, 0, 0x01); + else + exfat_chain_set(&dir, ei->start_clu, + EXFAT_B_TO_CLU(i_size_read(inode), sbi), ei->flags); + + dentries_per_clu = sbi->dentries_per_clu; + dentries_per_clu_bits = ilog2(dentries_per_clu); + + clu_offset = dentry >> dentries_per_clu_bits; + clu = exfat_chain_dup(&dir); + if (!clu) + return -ENOMEM; + + if (clu->flags == 0x03) { + clu->dir += clu_offset; + clu->size -= clu_offset; + } else { + /* hint_information */ + if (clu_offset > 0 && ei->hint_bmap.off != EOF_CLUSTER && + ei->hint_bmap.off > 0 && clu_offset >= ei->hint_bmap.off) { + clu_offset -= ei->hint_bmap.off; + clu->dir = ei->hint_bmap.clu; + } + + while (clu_offset > 0) { + if (exfat_get_next_cluster(sb, &(clu->dir))) { + ret = -EIO; + goto free_clu; + } + + clu_offset--; + } + } + + while (clu->dir != EOF_CLUSTER) { + i = dentry & (dentries_per_clu - 1); + + for ( ; i < dentries_per_clu; i++, dentry++) { + ep = exfat_get_dentry(sb, clu, i, &bh, §or); + if (!ep) { + ret = -EIO; + goto free_clu; + } + type = exfat_get_entry_type(ep); + + if (type == TYPE_UNUSED) { + brelse(bh); + break; + } + + if (type != TYPE_FILE && type != TYPE_DIR) { + brelse(bh); + continue; + } + + dir_entry->attr = le16_to_cpu(ep->file_attr); + + exfat_get_entry_time(ep, &tm, TM_CREATE); + dir_entry->create_timestamp.year = tm.year; + dir_entry->create_timestamp.month = tm.mon; + dir_entry->create_timestamp.day = tm.day; + dir_entry->create_timestamp.hour = tm.hour; + dir_entry->create_timestamp.minute = tm.min; + dir_entry->create_timestamp.second = tm.sec; + dir_entry->create_timestamp.milli_second = 0; + + exfat_get_entry_time(ep, &tm, TM_MODIFY); + dir_entry->modify_timestamp.year = tm.year; + dir_entry->modify_timestamp.month = tm.mon; + dir_entry->modify_timestamp.day = tm.day; + dir_entry->modify_timestamp.hour = tm.hour; + dir_entry->modify_timestamp.minute = tm.min; + dir_entry->modify_timestamp.second = tm.sec; + dir_entry->modify_timestamp.milli_second = 0; + + memset(&dir_entry->access_timestamp, 0, + sizeof(struct exfat_date_time)); + + *uni_name.name = 0x0; + exfat_get_uniname_from_ext_entry(sb, &dir, dentry, + uni_name.name); + exfat_nls_uni16s_to_vfsname(sb, &uni_name, + dir_entry->namebuf.lfn, + dir_entry->namebuf.lfnbuf_len); + brelse(bh); + + ep = exfat_get_dentry(sb, clu, i + 1, &bh, NULL); + if (!ep) { + ret = -EIO; + goto free_clu; + } + dir_entry->size = le64_to_cpu(ep->stream_valid_size); + brelse(bh); + + ei->hint_bmap.off = dentry >> dentries_per_clu_bits; + ei->hint_bmap.clu = clu->dir; + + ei->rwoffset = ++dentry; + goto free_clu; + } + + if (clu->flags == 0x03) { + if (--clu->size > 0) + clu->dir++; + else + clu->dir = EOF_CLUSTER; + } else { + if (exfat_get_next_cluster(sb, &(clu->dir))) { + ret = -EIO; + goto free_clu; + } + } + } + + dir_entry->namebuf.lfn[0] = '\0'; + ei->rwoffset = dentry; + +free_clu: + kfree(clu); + return ret; +} + +static void exfat_init_namebuf(struct exfat_dentry_namebuf *nb) +{ + nb->lfn = NULL; + nb->lfnbuf_len = 0; +} + +static int exfat_alloc_namebuf(struct exfat_dentry_namebuf *nb) +{ + nb->lfn = __getname(); + if (!nb->lfn) + return -ENOMEM; + nb->lfnbuf_len = MAX_VFSNAME_BUF_SIZE; + return 0; +} + +static void exfat_free_namebuf(struct exfat_dentry_namebuf *nb) +{ + if (!nb->lfn) + return; + + __putname(nb->lfn); + exfat_init_namebuf(nb); +} + +/* skip iterating emit_dots when dir is empty */ +#define ITER_POS_FILLED_DOTS (2) +static int exfat_iterate(struct file *filp, struct dir_context *ctx) +{ + struct inode *inode = filp->f_path.dentry->d_inode; + struct super_block *sb = inode->i_sb; + struct inode *tmp; + struct exfat_dir_entry de; + struct exfat_dentry_namebuf *nb = &(de.namebuf); + struct exfat_inode_info *ei = EXFAT_I(inode); + unsigned long inum; + loff_t cpos, i_pos; + int err = 0, fake_offset = 0; + + exfat_init_namebuf(nb); + mutex_lock(&EXFAT_SB(sb)->s_lock); + + cpos = ctx->pos; + if (!dir_emit_dots(filp, ctx)) + goto unlock; + + if (ctx->pos == ITER_POS_FILLED_DOTS) { + cpos = 0; + fake_offset = 1; + } + + if (cpos & (DENTRY_SIZE - 1)) { + err = -ENOENT; + goto unlock; + } + + /* name buffer should be allocated before use */ + err = exfat_alloc_namebuf(nb); + if (err) + goto unlock; +get_new: + ei->rwoffset = EXFAT_B_TO_DEN(cpos); + + if (cpos >= i_size_read(inode)) + goto end_of_dir; + + err = exfat_readdir(inode, &de); + if (err) { + /* + * At least we tried to read a sector. Move cpos to next sector + * position (should be aligned). + */ + if (err == -EIO) { + cpos += 1 << (sb->s_blocksize_bits); + cpos &= ~(sb->s_blocksize - 1); + } + + err = -EIO; + goto end_of_dir; + } + + cpos = EXFAT_DEN_TO_B(ei->rwoffset); + + if (!nb->lfn[0]) + goto end_of_dir; + + i_pos = ((loff_t)ei->start_clu << 32) | + ((ei->rwoffset - 1) & 0xffffffff); + tmp = exfat_iget(sb, i_pos); + if (tmp) { + inum = tmp->i_ino; + iput(tmp); + } else { + inum = iunique(sb, EXFAT_ROOT_INO); + } + + /* + * Before calling dir_emit(), sb_lock should be released. + * Because page fault can occur in dir_emit() when the size + * of buffer given from user is larger than one page size. + */ + mutex_unlock(&EXFAT_SB(sb)->s_lock); + if (!dir_emit(ctx, nb->lfn, strlen(nb->lfn), inum, + (de.attr & ATTR_SUBDIR) ? DT_DIR : DT_REG)) + goto out_unlocked; + mutex_lock(&EXFAT_SB(sb)->s_lock); + ctx->pos = cpos; + goto get_new; + +end_of_dir: + if (!cpos && fake_offset) + cpos = ITER_POS_FILLED_DOTS; + ctx->pos = cpos; +unlock: + mutex_unlock(&EXFAT_SB(sb)->s_lock); +out_unlocked: + /* + * To improve performance, free namebuf after unlock sb_lock. + * If namebuf is not allocated, this function do nothing + */ + exfat_free_namebuf(nb); + return err; +} + +const struct file_operations exfat_dir_operations = { + .llseek = generic_file_llseek, + .read = generic_read_dir, + .iterate = exfat_iterate, + .fsync = generic_file_fsync, +}; + +int exfat_alloc_new_dir(struct inode *inode, struct exfat_chain *clu) +{ + int ret; + + exfat_chain_set(clu, EOF_CLUSTER, 0, 0x03); + + ret = exfat_alloc_cluster(inode, 1, clu); + if (ret) + return ret; + + return exfat_zeroed_cluster(inode, clu->dir); +} + +static int exfat_calc_num_entries(struct exfat_uni_name *p_uniname) +{ + int len; + + len = p_uniname->name_len; + if (len == 0) + return 0; + + /* 1 file entry + 1 stream entry + name entries */ + return ((len - 1) / 15 + 3); +} + +/* + * input : dir, uni_name + * output : num_of_entry + */ +int exfat_get_num_entries(struct exfat_uni_name *p_uniname) +{ + int num_entries; + + num_entries = exfat_calc_num_entries(p_uniname); + if (num_entries == 0) + return -EINVAL; + + return num_entries; +} + +unsigned int exfat_get_entry_type(struct exfat_dentry *ep) +{ + if (ep->type == EXFAT_UNUSED) + return TYPE_UNUSED; + if (IS_EXFAT_DELETED(ep->type)) + return TYPE_DELETED; + if (ep->type == EXFAT_INVAL) + return TYPE_INVALID; + if (ep->type < 0xA0) { + if (ep->type == EXFAT_BITMAP) + return TYPE_BITMAP; + if (ep->type == EXFAT_UPCASE) + return TYPE_UPCASE; + if (ep->type == EXFAT_VOLUME) + return TYPE_VOLUME; + if (ep->type == EXFAT_FILE) { + if (le16_to_cpu(ep->file_attr) & ATTR_SUBDIR) + return TYPE_DIR; + return TYPE_FILE; + } + return TYPE_CRITICAL_PRI; + } + if (ep->type < 0xC0) { + if (ep->type == EXFAT_GUID) + return TYPE_GUID; + if (ep->type == EXFAT_PADDING) + return TYPE_PADDING; + if (ep->type == EXFAT_ACLTAB) + return TYPE_ACLTAB; + return TYPE_BENIGN_PRI; + } + if (ep->type < 0xE0) { + if (ep->type == EXFAT_STREAM) + return TYPE_STREAM; + if (ep->type == EXFAT_NAME) + return TYPE_EXTEND; + if (ep->type == EXFAT_ACL) + return TYPE_ACL; + return TYPE_CRITICAL_SEC; + } + return TYPE_BENIGN_SEC; +} + +static void exfat_set_entry_type(struct exfat_dentry *ep, unsigned int type) +{ + if (type == TYPE_UNUSED) { + ep->type = EXFAT_UNUSED; + } else if (type == TYPE_DELETED) { + ep->type &= EXFAT_DELETE; + } else if (type == TYPE_STREAM) { + ep->type = EXFAT_STREAM; + } else if (type == TYPE_EXTEND) { + ep->type = EXFAT_NAME; + } else if (type == TYPE_BITMAP) { + ep->type = EXFAT_BITMAP; + } else if (type == TYPE_UPCASE) { + ep->type = EXFAT_UPCASE; + } else if (type == TYPE_VOLUME) { + ep->type = EXFAT_VOLUME; + } else if (type == TYPE_DIR) { + ep->type = EXFAT_FILE; + ep->file_attr = ATTR_SUBDIR_LE; + } else if (type == TYPE_FILE) { + ep->type = EXFAT_FILE; + ep->file_attr = ATTR_ARCHIVE_LE; + } +} + +void exfat_get_entry_time(struct exfat_dentry *ep, struct exfat_timestamp *tp, + unsigned char mode) +{ + unsigned short t = 0x00, d = 0x21; + + switch (mode) { + case TM_CREATE: + t = le16_to_cpu(ep->file_create_time); + d = le16_to_cpu(ep->file_create_date); + break; + case TM_MODIFY: + t = le16_to_cpu(ep->file_modify_time); + d = le16_to_cpu(ep->file_modify_date); + break; + case TM_ACCESS: + t = le16_to_cpu(ep->file_access_time); + d = le16_to_cpu(ep->file_access_date); + break; + } + + tp->sec = (t & 0x001F) << 1; + tp->min = (t >> 5) & 0x003F; + tp->hour = (t >> 11); + tp->day = (d & 0x001F); + tp->mon = (d >> 5) & 0x000F; + tp->year = (d >> 9); +} + +void exfat_set_entry_time(struct exfat_dentry *ep, + struct exfat_timestamp *tp, unsigned char mode) +{ + unsigned short t, d; + + t = (tp->hour << 11) | (tp->min << 5) | (tp->sec >> 1); + d = (tp->year << 9) | (tp->mon << 5) | tp->day; + + switch (mode) { + case TM_CREATE: + ep->file_create_time = cpu_to_le16(t); + ep->file_create_date = cpu_to_le16(d); + break; + case TM_MODIFY: + ep->file_modify_time = cpu_to_le16(t); + ep->file_modify_date = cpu_to_le16(d); + break; + case TM_ACCESS: + ep->file_access_time = cpu_to_le16(t); + ep->file_access_date = cpu_to_le16(d); + break; + } +} + +static void exfat_init_file_entry(struct super_block *sb, + struct exfat_dentry *ep, unsigned int type) +{ + struct exfat_timestamp tm, *tp; + + exfat_set_entry_type(ep, type); + + tp = exfat_tm_now(EXFAT_SB(sb), &tm); + exfat_set_entry_time(ep, tp, TM_CREATE); + exfat_set_entry_time(ep, tp, TM_MODIFY); + exfat_set_entry_time(ep, tp, TM_ACCESS); + ep->file_create_time_ms = 0; + ep->file_modify_time_ms = 0; + ep->file_access_time_ms = 0; +} + +static void exfat_init_stream_entry(struct exfat_dentry *ep, + unsigned char flags, unsigned int start_clu, + unsigned long long size) +{ + exfat_set_entry_type(ep, TYPE_STREAM); + ep->stream_flags = flags; + ep->stream_start_clu = cpu_to_le32(start_clu); + ep->stream_valid_size = cpu_to_le64(size); + ep->stream_size = cpu_to_le64(size); +} + +static void exfat_init_name_entry(struct exfat_dentry *ep, + unsigned short *uniname) +{ + int i; + + exfat_set_entry_type(ep, TYPE_EXTEND); + ep->name_flags = 0x0; + + for (i = 0; i < 15; i++) { + ep->name_unicode[i] = cpu_to_le16(*uniname); + if (*uniname == 0x0) + break; + uniname++; + } +} + +int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir, + int entry, unsigned int type, unsigned int start_clu, + unsigned long long size) +{ + struct super_block *sb = inode->i_sb; + sector_t sector; + unsigned char flags; + struct exfat_dentry *ep; + struct buffer_head *bh; + int sync = IS_DIRSYNC(inode); + + flags = (type == TYPE_FILE) ? 0x01 : 0x03; + + /* + * We cannot use exfat_get_dentry_set here because file ep is not + * initialized yet. + */ + ep = exfat_get_dentry(sb, p_dir, entry, &bh, §or); + if (!ep) + return -EIO; + + exfat_init_file_entry(sb, ep, type); + exfat_update_bh(sb, bh, sync); + brelse(bh); + + ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh, §or); + if (!ep) + return -EIO; + + exfat_init_stream_entry(ep, flags, start_clu, size); + exfat_update_bh(sb, bh, sync); + brelse(bh); + + return 0; +} + +int update_dir_chksum(struct inode *inode, struct exfat_chain *p_dir, + int entry) +{ + struct super_block *sb = inode->i_sb; + int ret = 0; + int i, num_entries; + sector_t sector; + unsigned short chksum; + struct exfat_dentry *ep, *fep; + struct buffer_head *fbh, *bh; + + fep = exfat_get_dentry(sb, p_dir, entry, &fbh, §or); + if (!fep) + return -EIO; + + num_entries = fep->file_num_ext + 1; + chksum = exfat_calc_chksum_2byte(fep, DENTRY_SIZE, 0, CS_DIR_ENTRY); + + for (i = 1; i < num_entries; i++) { + ep = exfat_get_dentry(sb, p_dir, entry + i, &bh, NULL); + if (!ep) { + ret = -EIO; + goto out_unlock; + } + chksum = exfat_calc_chksum_2byte(ep, DENTRY_SIZE, chksum, + CS_DEFAULT); + brelse(bh); + } + + fep->file_checksum = cpu_to_le16(chksum); + exfat_update_bh(sb, fbh, IS_DIRSYNC(inode)); +out_unlock: + brelse(fbh); + return ret; +} + +int exfat_init_ext_entry(struct inode *inode, struct exfat_chain *p_dir, + int entry, int num_entries, struct exfat_uni_name *p_uniname) +{ + struct super_block *sb = inode->i_sb; + int i; + sector_t sector; + unsigned short *uniname = p_uniname->name; + struct exfat_dentry *ep; + struct buffer_head *bh; + int sync = IS_DIRSYNC(inode); + + ep = exfat_get_dentry(sb, p_dir, entry, &bh, §or); + if (!ep) + return -EIO; + + ep->file_num_ext = (unsigned char)(num_entries - 1); + exfat_update_bh(sb, bh, sync); + brelse(bh); + + ep = exfat_get_dentry(sb, p_dir, entry + 1, &bh, §or); + if (!ep) + return -EIO; + + ep->stream_name_len = p_uniname->name_len; + ep->stream_name_hash = cpu_to_le16(p_uniname->name_hash); + exfat_update_bh(sb, bh, sync); + brelse(bh); + + for (i = 2; i < num_entries; i++) { + ep = exfat_get_dentry(sb, p_dir, entry + i, &bh, §or); + if (!ep) + return -EIO; + + exfat_init_name_entry(ep, uniname); + exfat_update_bh(sb, bh, sync); + brelse(bh); + uniname += 15; + } + + update_dir_chksum(inode, p_dir, entry); + return 0; +} + +int exfat_remove_entries(struct inode *inode, struct exfat_chain *p_dir, + int entry, int order, int num_entries) +{ + struct super_block *sb = inode->i_sb; + int i; + sector_t sector; + struct exfat_dentry *ep; + struct buffer_head *bh; + + for (i = order; i < num_entries; i++) { + ep = exfat_get_dentry(sb, p_dir, entry + i, &bh, §or); + if (!ep) + return -EIO; + + exfat_set_entry_type(ep, TYPE_DELETED); + exfat_update_bh(sb, bh, IS_DIRSYNC(inode)); + brelse(bh); + } + + return 0; +} + +int exfat_update_dir_chksum_with_entry_set(struct super_block *sb, + struct exfat_entry_set_cache *es, int sync) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bh; + sector_t sec = es->sector; + unsigned int off = es->offset; + int chksum_type = CS_DIR_ENTRY, i, num_entries = es->num_entries; + unsigned int buf_off = (off - es->offset); + unsigned int remaining_byte_in_sector, copy_entries, clu; + unsigned short chksum = 0; + + for (i = 0; i < num_entries; i++) { + chksum = exfat_calc_chksum_2byte(&es->entries[i], DENTRY_SIZE, + chksum, chksum_type); + chksum_type = CS_DEFAULT; + } + + es->entries[0].file_checksum = cpu_to_le16(chksum); + + while (num_entries) { + /* write per sector base */ + remaining_byte_in_sector = (1 << sb->s_blocksize_bits) - off; + copy_entries = min_t(int, + EXFAT_B_TO_DEN(remaining_byte_in_sector), + num_entries); + bh = sb_bread(sb, sec); + if (!bh) + goto err_out; + memcpy(bh->b_data + off, + (unsigned char *)&es->entries[0] + buf_off, + EXFAT_DEN_TO_B(copy_entries)); + exfat_update_bh(sb, bh, sync); + brelse(bh); + num_entries -= copy_entries; + + if (num_entries) { + /* get next sector */ + if (exfat_is_last_sector_in_cluster(sbi, sec)) { + clu = exfat_sector_to_cluster(sbi, sec); + if (es->alloc_flag == 0x03) + clu++; + else if (exfat_get_next_cluster(sb, &clu)) + goto err_out; + sec = exfat_cluster_to_sector(sbi, clu); + } else { + sec++; + } + off = 0; + buf_off += EXFAT_DEN_TO_B(copy_entries); + } + } + + return 0; +err_out: + return -EIO; +} + +static int exfat_walk_fat_chain(struct super_block *sb, + struct exfat_chain *p_dir, unsigned int byte_offset, + unsigned int *clu) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + unsigned int clu_offset; + unsigned int cur_clu; + + clu_offset = EXFAT_B_TO_CLU(byte_offset, sbi); + cur_clu = p_dir->dir; + + if (p_dir->flags == 0x03) { + cur_clu += clu_offset; + } else { + while (clu_offset > 0) { + if (exfat_get_next_cluster(sb, &cur_clu)) + return -EIO; + if (cur_clu == EOF_CLUSTER) { + exfat_fs_error(sb, + "invalid dentry access beyond EOF (clu : %u, eidx : %d)", + p_dir->dir, + EXFAT_B_TO_DEN(byte_offset)); + return -EIO; + } + clu_offset--; + } + } + + *clu = cur_clu; + return 0; +} + +int exfat_find_location(struct super_block *sb, struct exfat_chain *p_dir, + int entry, sector_t *sector, int *offset) +{ + int ret; + unsigned int off, clu = 0; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + off = EXFAT_DEN_TO_B(entry); + + ret = exfat_walk_fat_chain(sb, p_dir, off, &clu); + if (ret) + return ret; + + /* byte offset in cluster */ + off = EXFAT_CLU_OFFSET(off, sbi); + + /* byte offset in sector */ + *offset = EXFAT_BLK_OFFSET(off, sb); + + /* sector offset in cluster */ + *sector = EXFAT_B_TO_BLK(off, sb); + *sector += exfat_cluster_to_sector(sbi, clu); + return 0; +} + +#define EXFAT_MAX_RA_SIZE (128*1024) +static int exfat_dir_readahead(struct super_block *sb, sector_t sec) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bh; + unsigned int max_ra_count = EXFAT_MAX_RA_SIZE >> sb->s_blocksize_bits; + unsigned int page_ra_count = PAGE_SIZE >> sb->s_blocksize_bits; + unsigned int adj_ra_count = max(sbi->sect_per_clus, page_ra_count); + unsigned int ra_count = min(adj_ra_count, max_ra_count); + + /* Read-ahead is not required */ + if (sbi->sect_per_clus == 1) + return 0; + + if (sec < sbi->data_start_sector) { + exfat_msg(sb, KERN_ERR, + "requested sector is invalid(sect:%llu, root:%llu)", + sec, sbi->data_start_sector); + return -EIO; + } + + /* Not sector aligned with ra_count, resize ra_count to page size */ + if ((sec - sbi->data_start_sector) & (ra_count - 1)) + ra_count = page_ra_count; + + bh = sb_find_get_block(sb, sec); + if (!bh || !buffer_uptodate(bh)) { + unsigned int i; + + for (i = 0; i < ra_count; i++) + sb_breadahead(sb, (sector_t)(sec + i)); + } + brelse(bh); + return 0; +} + +struct exfat_dentry *exfat_get_dentry(struct super_block *sb, + struct exfat_chain *p_dir, int entry, struct buffer_head **bh, + sector_t *sector) +{ + unsigned int dentries_per_page = EXFAT_B_TO_DEN(PAGE_SIZE); + int off; + sector_t sec; + + if (p_dir->dir == DIR_DELETED) { + exfat_msg(sb, KERN_ERR, "abnormal access to deleted dentry\n"); + return NULL; + } + + if (exfat_find_location(sb, p_dir, entry, &sec, &off)) + return NULL; + + if (p_dir->dir != FREE_CLUSTER && + !(entry & (dentries_per_page - 1))) + exfat_dir_readahead(sb, sec); + + *bh = sb_bread(sb, sec); + if (!*bh) + return NULL; + + if (sector) + *sector = sec; + return (struct exfat_dentry *)((*bh)->b_data + off); +} + +enum exfat_validate_dentry_mode { + ES_MODE_STARTED, + ES_MODE_GET_FILE_ENTRY, + ES_MODE_GET_STRM_ENTRY, + ES_MODE_GET_NAME_ENTRY, + ES_MODE_GET_CRITICAL_SEC_ENTRY, +}; + +static bool exfat_validate_entry(unsigned int type, + enum exfat_validate_dentry_mode *mode) +{ + if (type == TYPE_UNUSED || type == TYPE_DELETED) + return false; + + switch (*mode) { + case ES_MODE_STARTED: + if (type != TYPE_FILE && type != TYPE_DIR) + return false; + *mode = ES_MODE_GET_FILE_ENTRY; + return true; + case ES_MODE_GET_FILE_ENTRY: + if (type != TYPE_STREAM) + return false; + *mode = ES_MODE_GET_STRM_ENTRY; + return true; + case ES_MODE_GET_STRM_ENTRY: + if (type != TYPE_EXTEND) + return false; + *mode = ES_MODE_GET_NAME_ENTRY; + return true; + case ES_MODE_GET_NAME_ENTRY: + if (type == TYPE_STREAM) + return false; + if (type != TYPE_EXTEND) { + if (!(type & TYPE_CRITICAL_SEC)) + return false; + *mode = ES_MODE_GET_CRITICAL_SEC_ENTRY; + } + return true; + case ES_MODE_GET_CRITICAL_SEC_ENTRY: + if (type == TYPE_EXTEND || type == TYPE_STREAM) + return false; + if ((type & TYPE_CRITICAL_SEC) != TYPE_CRITICAL_SEC) + return false; + return true; + default: + WARN_ON_ONCE(1); + return false; + } +} + +/* + * Returns a set of dentries for a file or dir. + * + * Note that this is a copy (dump) of dentries so that user should + * call write_entry_set() to apply changes made in this entry set + * to the real device. + * + * in: + * sb+p_dir+entry: indicates a file/dir + * type: specifies how many dentries should be included. + * out: + * file_ep: will point the first dentry(= file dentry) on success + * return: + * pointer of entry set on success, + * NULL on failure. + */ +struct exfat_entry_set_cache *exfat_get_dentry_set(struct super_block *sb, + struct exfat_chain *p_dir, int entry, unsigned int type, + struct exfat_dentry **file_ep) +{ + int ret; + unsigned int off, byte_offset, clu = 0; + unsigned int entry_type; + sector_t sec; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_entry_set_cache *es = NULL; + struct exfat_dentry *ep, *pos; + unsigned char num_entries; + enum exfat_validate_dentry_mode mode = ES_MODE_STARTED; + struct buffer_head *bh; + + /* FIXME : is available in error case? */ + if (p_dir->dir == DIR_DELETED) { + exfat_msg(sb, KERN_ERR, "access to deleted dentry\n"); + return NULL; + } + + byte_offset = EXFAT_DEN_TO_B(entry); + ret = exfat_walk_fat_chain(sb, p_dir, byte_offset, &clu); + if (ret) + return NULL; + + /* byte offset in cluster */ + byte_offset = EXFAT_CLU_OFFSET(byte_offset, sbi); + + /* byte offset in sector */ + off = EXFAT_BLK_OFFSET(byte_offset, sb); + + /* sector offset in cluster */ + sec = EXFAT_B_TO_BLK(byte_offset, sb); + sec += exfat_cluster_to_sector(sbi, clu); + + bh = sb_bread(sb, sec); + if (!bh) + goto err_out; + + ep = (struct exfat_dentry *)(bh->b_data + off); + entry_type = exfat_get_entry_type(ep); + + if (entry_type != TYPE_FILE && entry_type != TYPE_DIR) + goto err_out; + + if (type == ES_ALL_ENTRIES) + num_entries = ep->file_num_ext + 1; + else + num_entries = type; + + es = kmalloc(struct_size(es, entries, num_entries), GFP_KERNEL); + if (!es) + goto err_out; + + es->num_entries = num_entries; + es->sector = sec; + es->offset = off; + es->alloc_flag = p_dir->flags; + + pos = &es->entries[0]; + + while (num_entries) { + if (!exfat_validate_entry(exfat_get_entry_type(ep), &mode)) + goto err_out; + + /* copy dentry */ + memcpy(pos, ep, sizeof(struct exfat_dentry)); + + if (--num_entries == 0) + break; + + if (((off + DENTRY_SIZE) & (sb->s_blocksize - 1)) < + (off & (sb->s_blocksize - 1))) { + /* get the next sector */ + if (exfat_is_last_sector_in_cluster(sbi, sec)) { + if (es->alloc_flag == 0x03) + clu++; + else if (exfat_get_next_cluster(sb, &clu)) + goto err_out; + sec = exfat_cluster_to_sector(sbi, clu); + } else { + sec++; + } + + brelse(bh); + bh = sb_bread(sb, sec); + if (!bh) + goto err_out; + off = 0; + ep = (struct exfat_dentry *)bh->b_data; + } else { + ep++; + off += DENTRY_SIZE; + } + pos++; + } + + if (file_ep) + *file_ep = &es->entries[0]; + brelse(bh); + + return es; +err_out: + kfree(es); + brelse(bh); + return NULL; +} + +static int exfat_extract_uni_name(struct exfat_dentry *ep, + unsigned short *uniname) +{ + int i, len = 0; + + for (i = 0; i < 15; i++) { + *uniname = le16_to_cpu(ep->name_unicode[i]); + if (*uniname == 0x0) + return len; + uniname++; + len++; + } + + *uniname = 0x0; + return len; + +} + +#define DIRENT_STEP_FILE (0) +#define DIRENT_STEP_STRM (1) +#define DIRENT_STEP_NAME (2) +#define DIRENT_STEP_SECD (3) + +/* + * return values: + * >= 0 : return dir entiry position with the name in dir + * -EEXIST : (root dir, ".") it is the root dir itself + * -ENOENT : entry with the name does not exist + * -EIO : I/O error + */ +int exfat_find_dir_entry(struct super_block *sb, struct exfat_inode_info *ei, + struct exfat_chain *p_dir, struct exfat_uni_name *p_uniname, + int num_entries, unsigned int type) +{ + int i, rewind = 0, dentry = 0, end_eidx = 0, num_ext = 0, len; + int order, step, name_len = 0; + int dentries_per_clu, num_empty = 0; + unsigned int entry_type; + unsigned short entry_uniname[16], *uniname = NULL, unichar; + struct exfat_chain *clu; + struct exfat_dentry *ep; + struct exfat_hint *hint_stat = &ei->hint_stat; + struct exfat_hint_femp candi_empty; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bh = NULL; + + dentries_per_clu = sbi->dentries_per_clu; + + clu = exfat_chain_dup(p_dir); + if (!clu) + return -ENOMEM; + + if (hint_stat->eidx) { + clu->dir = hint_stat->clu; + dentry = hint_stat->eidx; + end_eidx = dentry; + } + + candi_empty.eidx = EXFAT_HINT_NONE; +rewind: + order = 0; + step = DIRENT_STEP_FILE; + while (clu->dir != EOF_CLUSTER) { + i = dentry & (dentries_per_clu - 1); + for (; i < dentries_per_clu; i++, dentry++) { + if (rewind && dentry == end_eidx) + goto not_found; + + ep = exfat_get_dentry(sb, clu, i, &bh, NULL); + if (!ep) { + kfree(clu); + return -EIO; + } + + entry_type = exfat_get_entry_type(ep); + + if (entry_type == TYPE_UNUSED || + entry_type == TYPE_DELETED) { + step = DIRENT_STEP_FILE; + + num_empty++; + if (candi_empty.eidx == EXFAT_HINT_NONE && + num_empty == 1) { + exfat_chain_set(&candi_empty.cur, + clu->dir, clu->size, + clu->flags); + } + + if (candi_empty.eidx == EXFAT_HINT_NONE && + num_empty >= num_entries) { + candi_empty.eidx = + dentry - (num_empty - 1); + WARN_ON(candi_empty.eidx < 0); + candi_empty.count = num_empty; + + if (ei->hint_femp.eidx == + EXFAT_HINT_NONE || + candi_empty.eidx <= + ei->hint_femp.eidx) { + memcpy(&ei->hint_femp, + &candi_empty, + sizeof(candi_empty)); + } + } + + brelse(bh); + if (entry_type == TYPE_UNUSED) + goto not_found; + continue; + } + + num_empty = 0; + candi_empty.eidx = EXFAT_HINT_NONE; + + if (entry_type == TYPE_FILE || entry_type == TYPE_DIR) { + step = DIRENT_STEP_FILE; + if (type == TYPE_ALL || type == entry_type) { + num_ext = ep->file_num_ext; + step = DIRENT_STEP_STRM; + } + brelse(bh); + continue; + } + + if (entry_type == TYPE_STREAM) { + unsigned short name_hash; + + if (step != DIRENT_STEP_STRM) { + step = DIRENT_STEP_FILE; + brelse(bh); + continue; + } + step = DIRENT_STEP_FILE; + name_hash = le16_to_cpu(ep->stream_name_hash); + if (p_uniname->name_hash == name_hash && + p_uniname->name_len == + ep->stream_name_len) { + step = DIRENT_STEP_NAME; + order = 1; + name_len = 0; + } + brelse(bh); + continue; + } + + brelse(bh); + if (entry_type == TYPE_EXTEND) { + if (step != DIRENT_STEP_NAME) { + step = DIRENT_STEP_FILE; + continue; + } + + if (++order == 2) + uniname = p_uniname->name; + else + uniname += 15; + + len = exfat_extract_uni_name( + ep, entry_uniname); + name_len += len; + + unichar = *(uniname+len); + *(uniname+len) = 0x0; + + if (exfat_nls_cmp_uniname(sb, uniname, + entry_uniname)) { + step = DIRENT_STEP_FILE; + } else if (name_len == p_uniname->name_len) { + if (order == num_ext) + goto found; + step = DIRENT_STEP_SECD; + } + + *(uniname+len) = unichar; + continue; + } + + if (entry_type & + (TYPE_CRITICAL_SEC | TYPE_BENIGN_SEC)) { + if (step == DIRENT_STEP_SECD) { + if (++order == num_ext) + goto found; + continue; + } + } + step = DIRENT_STEP_FILE; + } + + if (clu->flags == 0x03) { + if (--clu->size > 0) + clu->dir++; + else + clu->dir = EOF_CLUSTER; + } else { + if (exfat_get_next_cluster(sb, &clu->dir)) { + kfree(clu); + return -EIO; + } + } + } + +not_found: + /* + * We started at not 0 index,so we should try to find target + * from 0 index to the index we started at. + */ + if (!rewind && end_eidx) { + rewind = 1; + dentry = 0; + clu->dir = p_dir->dir; + /* reset empty hint */ + num_empty = 0; + candi_empty.eidx = EXFAT_HINT_NONE; + goto rewind; + } + + /* initialized hint_stat */ + hint_stat->clu = p_dir->dir; + hint_stat->eidx = 0; + kfree(clu); + return -ENOENT; + +found: + /* next dentry we'll find is out of this cluster */ + if (!((dentry + 1) & (dentries_per_clu - 1))) { + int ret = 0; + + if (clu->flags == 0x03) { + if (--clu->size > 0) + clu->dir++; + else + clu->dir = EOF_CLUSTER; + } else { + ret = exfat_get_next_cluster(sb, &clu->dir); + } + + if (ret || clu->dir != EOF_CLUSTER) { + /* just initialized hint_stat */ + hint_stat->clu = p_dir->dir; + hint_stat->eidx = 0; + return (dentry - num_ext); + } + } + + hint_stat->clu = clu->dir; + hint_stat->eidx = dentry + 1; + kfree(clu); + return dentry - num_ext; +} + +int exfat_count_ext_entries(struct super_block *sb, struct exfat_chain *p_dir, + int entry, struct exfat_dentry *ep) +{ + int i, count = 0; + unsigned int type; + struct exfat_dentry *ext_ep; + struct buffer_head *bh; + + for (i = 0, entry++; i < ep->file_num_ext; i++, entry++) { + ext_ep = exfat_get_dentry(sb, p_dir, entry, &bh, NULL); + if (!ext_ep) + return -EIO; + + type = exfat_get_entry_type(ext_ep); + brelse(bh); + if (type == TYPE_EXTEND || type == TYPE_STREAM) + count++; + else + return count; + } + + return count; +} + +void exfat_get_uniname_from_ext_entry(struct super_block *sb, + struct exfat_chain *p_dir, int entry, unsigned short *uniname) +{ + int i; + struct exfat_dentry *ep; + struct exfat_entry_set_cache *es; + + es = exfat_get_dentry_set(sb, p_dir, entry, ES_ALL_ENTRIES, &ep); + if (!es) + return; + + if (es->num_entries < 3) + goto out; + + ep += 2; + + /* + * First entry : file entry + * Second entry : stream-extension entry + * Third entry : first file-name entry + * So, the index of first file-name dentry should start from 2. + */ + for (i = 2; i < es->num_entries; i++, ep++) { + /* end of name entry */ + if (exfat_get_entry_type(ep) != TYPE_EXTEND) + goto out; + + exfat_extract_uni_name(ep, uniname); + uniname += 15; + } + +out: + kfree(es); +} + +int exfat_count_dir_entries(struct super_block *sb, struct exfat_chain *p_dir) +{ + int i, count = 0; + int dentries_per_clu; + unsigned int entry_type; + struct exfat_chain *clu; + struct exfat_dentry *ep; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bh; + + dentries_per_clu = sbi->dentries_per_clu; + + clu = exfat_chain_dup(p_dir); + + while (clu->dir != EOF_CLUSTER) { + for (i = 0; i < dentries_per_clu; i++) { + ep = exfat_get_dentry(sb, clu, i, &bh, NULL); + if (!ep) { + count = -EIO; + goto free_clu; + } + entry_type = exfat_get_entry_type(ep); + brelse(bh); + + if (entry_type == TYPE_UNUSED) + goto free_clu; + if (entry_type != TYPE_DIR) + continue; + count++; + } + + if (clu->flags == 0x03) { + if (--clu->size > 0) + clu->dir++; + else + clu->dir = EOF_CLUSTER; + } else { + if (exfat_get_next_cluster(sb, &(clu->dir))) { + count = -EIO; + goto free_clu; + } + } + } +free_clu: + kfree(clu); + return count; +} From patchwork Tue Nov 19 09:37:10 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11251639 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6BEFC14C0 for ; Tue, 19 Nov 2019 09:41:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3828F2230C for ; Tue, 19 Nov 2019 09:41:16 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="bJxM0+lH" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727514AbfKSJk2 (ORCPT ); Tue, 19 Nov 2019 04:40:28 -0500 Received: from mailout2.samsung.com ([203.254.224.25]:40674 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727455AbfKSJk1 (ORCPT ); Tue, 19 Nov 2019 04:40:27 -0500 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20191119094024epoutp020f738416d205ab4689d8361b71f6b610~Yhu2kvLOB0392003920epoutp02E for ; Tue, 19 Nov 2019 09:40:24 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20191119094024epoutp020f738416d205ab4689d8361b71f6b610~Yhu2kvLOB0392003920epoutp02E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574156425; bh=UynEUjd2J6AJBENA5flggnmE+/uNET28DrdikmIOav4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bJxM0+lHm1snwimsEnnMZCnnDHOjB1Nxi8DA2tGoaBhhuho4/VSIJDXV+Unie55n2 Jk8qo+RwL2AuZ/hBlkiE4u/7dQwLUQj4R5/y0X2kor0CnchIIiIfZcOcCfKqtlykoo AjxrCQopEQC3HaAB2p7o2UPIvzuEMYW5zlPkHytM= Received: from epsnrtp4.localdomain (unknown [182.195.42.165]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20191119094024epcas1p1d0ee8b3a257efb0fa6fc80197a3e27be~Yhu2FBwrt0953109531epcas1p1y; Tue, 19 Nov 2019 09:40:24 +0000 (GMT) Received: from epsmges1p2.samsung.com (unknown [182.195.40.165]) by epsnrtp4.localdomain (Postfix) with ESMTP id 47HLQv3rMSzMqYkh; Tue, 19 Nov 2019 09:40:23 +0000 (GMT) Received: from epcas1p3.samsung.com ( [182.195.41.47]) by epsmges1p2.samsung.com (Symantec Messaging Gateway) with SMTP id 5B.73.04235.788B3DD5; Tue, 19 Nov 2019 18:40:23 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p1.samsung.com (KnoxPortal) with ESMTPA id 20191119094023epcas1p157ea65111e4564d5da2f64f168440e2a~Yhu0xkJ0F0953109531epcas1p1t; Tue, 19 Nov 2019 09:40:23 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119094023epsmtrp1866ca99a99cd538a79e23b0734327db6~Yhu0w7pVv0081500815epsmtrp1b; Tue, 19 Nov 2019 09:40:23 +0000 (GMT) X-AuditID: b6c32a36-defff7000000108b-fe-5dd3b8871094 Received: from epsmtip2.samsung.com ( [182.195.34.31]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id 5D.92.03814.688B3DD5; Tue, 19 Nov 2019 18:40:22 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20191119094022epsmtip235dc2583aa03976649d0d7c31fe727cf~Yhu0ln2fy0708907089epsmtip2D; Tue, 19 Nov 2019 09:40:22 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, Namjae Jeon Subject: [PATCH v3 05/13] exfat: add file operations Date: Tue, 19 Nov 2019 04:37:10 -0500 Message-Id: <20191119093718.3501-6-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119093718.3501-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA01Se0hTURzm7G5319HqMq1O9lqXijTMzbl5K62gqAvNkAIDy+yiFyfuxe6d PSQSsjQptYgES0iToCyWuXRzmktnTYJKhdTK6IFZ4rMyBV9tu4v67/f7zvf7vu/8zsEQ2Xk0 HMsycozFSOsJVCKsb4uIjipwdKcqLjetJdu+XBWT527bUPJuTbuA7Ol/i5BNzR1CsrvxJkou lA+KyOmys6R93iMiu8bGhbsklLO8X0y1VNwXU66+PJQqtt8DVN2LXOrnozVUa8MwSr37Wi9M wlL08TqGzmAscsaYbsrIMmYmEPsPpe1OU2sUyijlVjKOkBtpA5NA7NEmRe3N0vsiEvIcWm/1 QUk0yxLRO+ItJivHyHUmlksgGHOG3qxUmLewtIG1GjO3pJsM25QKRYzaxzyu13157wXmae3J IdcnNA88SygCIRjEY2FZRa2wCEgwGe4A8Fn1NQHf/ACw7o0L5ZvfAFZf7AN/RzrcC0FWM4C1 16fE/oPASP6r9UUAw1B8M5y1L/XDYfhOWHfDHbBAcA+A336VBvihPqGb/U0BUSG+ARYXTAj8 tRTfDh2TNgFvthbWPHQj/joEj4efbtwBfiGIe1FYOdcQTLQHtrvaRHwdCoee28V8HQ6/l1wQ +wNBPBdOtCA8XOgLMRW8vwr22R6K/BQEj4C2xmgeXgedMxUBdQRfDEcnL4l4FSksvCDjKb7E XW3BlCthUcF40JSC+WNuEb+eEgC9nlqkFKwp/+dwC4B7YBljZg2ZDKs0x/z/YI9A4AtGahyg 6qW2FeAYIBZJveu7U2UiOoc9ZWgFEEOIMOmBz69TZdIM+tRpxmJKs1j1DNsK1L5FXkHCl6ab fB/ayKUp1TEqlYqM1cRp1CpiuRSb7kyV4Zk0x2QzjJmx/J0TYCHheSCPHMite/D4ZaF1R8FC vJujvYkjXLGn83hysa5n0+TTuXcdtCfxifMo9V1yIrmknHPipasqG/dFxshWHKlaMpKyKLHz YG8pN/PY62wZ6v0wbnNIXo/1vv8YQZzZmD1fMzzQUKYZ3zCFjob+OqxNlg/iL9jNY9Zj7Qc3 umZX27sIIaujlZGIhaX/AGSUo9aYAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrPLMWRmVeSWpSXmKPExsWy7bCSvG7bjsuxBts/WlkcfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9RZb/h1htbj0/gOLA5fHzll32T32z13D 7rH7ZgObR9+WVYwem09Xe3zeJOdxaPsbNo/bz7axBHBEcdmkpOZklqUW6dslcGU8vnOCseCH T8Wr3Q/ZGhiP2XYxcnJICJhInDzwn6mLkYtDSGA3o8S1a5fZIRLSEsdOnGHuYuQAsoUlDh8u hqj5wChxov8uE0icTUBb4s8WUZByEQFHid5dh1lAapgFzjFK7Hy2jBEkIQy0YM7dPWA2i4Cq RF/7RyYQm1fAWmLH1/VMELvkJVZvOMAMYnMK2Eg8nA3RKwRU0/iomX0CI98CRoZVjJKpBcW5 6bnFhgVGeanlesWJucWleel6yfm5mxjBwaqltYPxxIn4Q4wCHIxKPLwnVC7HCrEmlhVX5h5i lOBgVhLh9Xt0IVaINyWxsiq1KD++qDQntfgQozQHi5I4r3z+sUghgfTEktTs1NSC1CKYLBMH p1QDo53sjrBavW/P1Ldp7meeu/v01nzmR1Ea1ybVNgqW/2F9p+Bgb8aqMzctPLK5KVORwdX5 5gb37bwfDJbuWL9v7d95fvvb3YvlThjkzbD/06642nqqtcViOb0ttxcqHDtjGR7sfexx4qqt kUwpSU4BM4VSjmTZzG147ijw6uyyk3LdarL1YTfPKLEUZyQaajEXFScCAIIfHCJSAgAA X-CMS-MailID: 20191119094023epcas1p157ea65111e4564d5da2f64f168440e2a X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119094023epcas1p157ea65111e4564d5da2f64f168440e2a References: <20191119093718.3501-1-namjae.jeon@samsung.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This adds the implementation of file operations for exfat. Signed-off-by: Namjae Jeon Signed-off-by: Sungjong Seo --- fs/exfat/file.c | 346 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 fs/exfat/file.c diff --git a/fs/exfat/file.c b/fs/exfat/file.c new file mode 100644 index 000000000000..5afd65a36eb5 --- /dev/null +++ b/fs/exfat/file.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "exfat_raw.h" +#include "exfat_fs.h" + +static int exfat_cont_expand(struct inode *inode, loff_t size) +{ + struct address_space *mapping = inode->i_mapping; + loff_t start = i_size_read(inode), count = size - i_size_read(inode); + int err, err2; + + err = generic_cont_expand_simple(inode, size); + if (err) + return err; + + inode->i_ctime = inode->i_mtime = current_time(inode); + mark_inode_dirty(inode); + + if (!IS_SYNC(inode)) + return 0; + + err = filemap_fdatawrite_range(mapping, start, start + count - 1); + err2 = sync_mapping_buffers(mapping); + if (!err) + err = err2; + err2 = write_inode_now(inode, 1); + if (!err) + err = err2; + if (err) + return err; + + return filemap_fdatawait_range(mapping, start, start + count - 1); +} + +static int exfat_allow_set_time(struct exfat_sb_info *sbi, struct inode *inode) +{ + mode_t allow_utime = sbi->options.allow_utime; + + if (!uid_eq(current_fsuid(), inode->i_uid)) { + if (in_group_p(inode->i_gid)) + allow_utime >>= 3; + if (allow_utime & MAY_WRITE) + return 1; + } + + /* use a default check */ + return 0; +} + +static int exfat_sanitize_mode(const struct exfat_sb_info *sbi, + struct inode *inode, umode_t *mode_ptr) +{ + mode_t i_mode, mask, perm; + + i_mode = inode->i_mode; + + if (S_ISREG(i_mode) || S_ISLNK(i_mode)) + mask = sbi->options.fs_fmask; + else + mask = sbi->options.fs_dmask; + + perm = *mode_ptr & ~(S_IFMT | mask); + + /* Of the r and x bits, all (subject to umask) must be present.*/ + if ((perm & 0555) != (i_mode & 0555)) + return -EPERM; + + if (exfat_mode_can_hold_ro(inode)) { + /* + * Of the w bits, either all (subject to umask) or none must + * be present. + */ + if ((perm & 0222) && ((perm & 0222) != (0222 & ~mask))) + return -EPERM; + } else { + /* + * If exfat_mode_can_hold_ro(inode) is false, can't change + * w bits. + */ + if ((perm & 0222) != (0222 & ~mask)) + return -EPERM; + } + + *mode_ptr &= S_IFMT | perm; + + return 0; +} + +/* resize the file length */ +int __exfat_truncate(struct inode *inode, loff_t new_size) +{ + unsigned int num_clusters_new, num_clusters_phys; + unsigned int last_clu = FREE_CLUSTER; + struct exfat_chain clu; + struct exfat_timestamp tm; + struct exfat_dentry *ep, *ep2; + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_inode_info *ei = EXFAT_I(inode); + struct exfat_entry_set_cache *es = NULL; + int evict = (ei->dir.dir == DIR_DELETED) ? 1 : 0; + + /* check if the given file ID is opened */ + if (ei->type != TYPE_FILE && ei->type != TYPE_DIR) + return -EPERM; + + exfat_set_vol_flags(sb, VOL_DIRTY); + + num_clusters_new = EXFAT_B_TO_CLU_ROUND_UP(i_size_read(inode), sbi); + num_clusters_phys = + EXFAT_B_TO_CLU_ROUND_UP(EXFAT_I(inode)->i_size_ondisk, sbi); + + exfat_chain_set(&clu, ei->start_clu, num_clusters_phys, ei->flags); + + if (new_size > 0) { + /* + * Truncate FAT chain num_clusters after the first cluster + * num_clusters = min(new, phys); + */ + unsigned int num_clusters = + min(num_clusters_new, num_clusters_phys); + + /* + * Follow FAT chain + * (defensive coding - works fine even with corrupted FAT table + */ + if (clu.flags == 0x03) { + clu.dir += num_clusters; + clu.size -= num_clusters; + } else { + while (num_clusters > 0) { + last_clu = clu.dir; + if (exfat_get_next_cluster(sb, &(clu.dir))) + return -EIO; + + num_clusters--; + clu.size--; + } + } + } else { + ei->flags = 0x03; + ei->start_clu = EOF_CLUSTER; + } + + i_size_write(inode, new_size); + + if (ei->type == TYPE_FILE) + ei->attr |= ATTR_ARCHIVE; + + /* update the directory entry */ + if (!evict) { + es = exfat_get_dentry_set(sb, &(ei->dir), ei->entry, + ES_ALL_ENTRIES, &ep); + if (!es) + return -EIO; + ep2 = ep + 1; + + exfat_set_entry_time(ep, exfat_tm_now(EXFAT_SB(sb), &tm), + TM_MODIFY); + ep->file_attr = cpu_to_le16(ei->attr); + + /* File size should be zero if there is no cluster allocated */ + if (ei->start_clu == EOF_CLUSTER) + ep->stream_valid_size = ep->stream_size = 0; + else { + ep->stream_valid_size = cpu_to_le64(new_size); + ep->stream_size = ep->stream_valid_size; + } + + if (new_size == 0) { + /* Any directory can not be truncated to zero */ + WARN_ON(ei->type != TYPE_FILE); + + ep2->stream_flags = 0x01; + ep2->stream_start_clu = FREE_CLUSTER; + } + + if (exfat_update_dir_chksum_with_entry_set(sb, es, + inode_needs_sync(inode))) + return -EIO; + kfree(es); + } + + /* cut off from the FAT chain */ + if (ei->flags == 0x01 && last_clu != FREE_CLUSTER && + last_clu != EOF_CLUSTER) { + if (exfat_ent_set(sb, last_clu, EOF_CLUSTER)) + return -EIO; + } + + /* invalidate cache and free the clusters */ + /* clear exfat cache */ + exfat_cache_inval_inode(inode); + + /* hint information */ + ei->hint_bmap.off = EOF_CLUSTER; + ei->hint_bmap.clu = EOF_CLUSTER; + if (ei->rwoffset > new_size) + ei->rwoffset = new_size; + + /* hint_stat will be used if this is directory. */ + ei->hint_stat.eidx = 0; + ei->hint_stat.clu = ei->start_clu; + ei->hint_femp.eidx = EXFAT_HINT_NONE; + + /* free the clusters */ + if (exfat_free_cluster(inode, &clu)) + return -EIO; + + exfat_set_vol_flags(sb, VOL_CLEAN); + + return 0; +} + +void exfat_truncate(struct inode *inode, loff_t size) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + unsigned int blocksize = 1 << inode->i_blkbits; + loff_t aligned_size; + int err; + + mutex_lock(&sbi->s_lock); + if (EXFAT_I(inode)->start_clu == 0) { + /* + * Empty start_clu != ~0 (not allocated) + */ + exfat_fs_error(sb, "tried to truncate zeroed cluster."); + goto out; + } + + err = __exfat_truncate(inode, i_size_read(inode)); + if (err) + goto out; + + inode->i_ctime = inode->i_mtime = current_time(inode); + if (IS_DIRSYNC(inode)) + exfat_sync_inode(inode); + else + mark_inode_dirty(inode); + + inode->i_blocks = ((i_size_read(inode) + (sbi->cluster_size - 1)) & + ~(sbi->cluster_size - 1)) >> inode->i_blkbits; +out: + aligned_size = i_size_read(inode); + if (aligned_size & (blocksize - 1)) { + aligned_size |= (blocksize - 1); + aligned_size++; + } + + if (EXFAT_I(inode)->i_size_ondisk > i_size_read(inode)) + EXFAT_I(inode)->i_size_ondisk = aligned_size; + + if (EXFAT_I(inode)->i_size_aligned > i_size_read(inode)) + EXFAT_I(inode)->i_size_aligned = aligned_size; + mutex_unlock(&sbi->s_lock); +} + +int exfat_getattr(const struct path *path, struct kstat *stat, + unsigned int request_mask, unsigned int query_flags) +{ + struct inode *inode = d_backing_inode(path->dentry); + + generic_fillattr(inode, stat); + stat->blksize = EXFAT_SB(inode->i_sb)->cluster_size; + return 0; +} + +int exfat_setattr(struct dentry *dentry, struct iattr *attr) +{ + struct exfat_sb_info *sbi = EXFAT_SB(dentry->d_sb); + struct inode *inode = dentry->d_inode; + unsigned int ia_valid; + int error; + + if ((attr->ia_valid & ATTR_SIZE) && + attr->ia_size > i_size_read(inode)) { + error = exfat_cont_expand(inode, attr->ia_size); + if (error || attr->ia_valid == ATTR_SIZE) + return error; + attr->ia_valid &= ~ATTR_SIZE; + } + + /* Check for setting the inode time. */ + ia_valid = attr->ia_valid; + if ((ia_valid & (ATTR_MTIME_SET | ATTR_ATIME_SET | ATTR_TIMES_SET)) && + exfat_allow_set_time(sbi, inode)) { + attr->ia_valid &= ~(ATTR_MTIME_SET | ATTR_ATIME_SET | + ATTR_TIMES_SET); + } + + error = setattr_prepare(dentry, attr); + attr->ia_valid = ia_valid; + if (error) + return error; + + if (((attr->ia_valid & ATTR_UID) && + !uid_eq(attr->ia_uid, sbi->options.fs_uid)) || + ((attr->ia_valid & ATTR_GID) && + !gid_eq(attr->ia_gid, sbi->options.fs_gid)) || + ((attr->ia_valid & ATTR_MODE) && + (attr->ia_mode & ~(S_IFREG | S_IFLNK | S_IFDIR | 0777)))) + return -EPERM; + + /* + * We don't return -EPERM here. Yes, strange, but this is too + * old behavior. + */ + if (attr->ia_valid & ATTR_MODE) { + if (exfat_sanitize_mode(sbi, inode, &attr->ia_mode) < 0) + attr->ia_valid &= ~ATTR_MODE; + } + + if (attr->ia_valid & ATTR_SIZE) { + down_write(&EXFAT_I(inode)->truncate_lock); + truncate_setsize(inode, attr->ia_size); + exfat_truncate(inode, attr->ia_size); + up_write(&EXFAT_I(inode)->truncate_lock); + } + + setattr_copy(inode, attr); + mark_inode_dirty(inode); + + return error; +} + +const struct file_operations exfat_file_operations = { + .llseek = generic_file_llseek, + .read_iter = generic_file_read_iter, + .write_iter = generic_file_write_iter, + .mmap = generic_file_mmap, + .fsync = generic_file_fsync, + .splice_read = generic_file_splice_read, +}; + +const struct inode_operations exfat_file_inode_operations = { + .setattr = exfat_setattr, + .getattr = exfat_getattr, +}; From patchwork Tue Nov 19 09:37:11 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11251641 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 275D714C0 for ; Tue, 19 Nov 2019 09:41:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E617C2230C for ; Tue, 19 Nov 2019 09:41:17 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="rhuyykRF" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727791AbfKSJlR (ORCPT ); Tue, 19 Nov 2019 04:41:17 -0500 Received: from mailout1.samsung.com ([203.254.224.24]:20905 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727474AbfKSJk2 (ORCPT ); Tue, 19 Nov 2019 04:40:28 -0500 Received: from epcas1p2.samsung.com (unknown [182.195.41.46]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20191119094025epoutp01693a951d089e0df10292398a8346a363~Yhu27ekeq0930609306epoutp01E for ; Tue, 19 Nov 2019 09:40:25 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20191119094025epoutp01693a951d089e0df10292398a8346a363~Yhu27ekeq0930609306epoutp01E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574156425; bh=moFSjqom8tMKVgnAIE+bliJo2nyGZ77U3BbjnQGWtEE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rhuyykRFmUWYHocNOox+UxweK3oHvQDGxU/fKILTIyYqissFklwU5Pcpy5tZ7jwMH GUuxp2mT+BAZkQbbP5dZBUtoh4/8YMz74OoAz47PuBADofeWrsS5yCOS01iZ+UrXh0 s2YEMGkd2i8fhu8tB1nO8j3OboTzIVJLZiBLaIKs= Received: from epsnrtp1.localdomain (unknown [182.195.42.162]) by epcas1p3.samsung.com (KnoxPortal) with ESMTP id 20191119094025epcas1p38d0c76c7ff87ad5c9e428e5d67ebe645~Yhu2sv9a60934409344epcas1p3a; Tue, 19 Nov 2019 09:40:25 +0000 (GMT) Received: from epsmges1p1.samsung.com (unknown [182.195.40.159]) by epsnrtp1.localdomain (Postfix) with ESMTP id 47HLQw0TwkzMqYlh; Tue, 19 Nov 2019 09:40:24 +0000 (GMT) Received: from epcas1p1.samsung.com ( [182.195.41.45]) by epsmges1p1.samsung.com (Symantec Messaging Gateway) with SMTP id 12.33.04072.788B3DD5; Tue, 19 Nov 2019 18:40:24 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p4.samsung.com (KnoxPortal) with ESMTPA id 20191119094023epcas1p4976d19f4b9a859ba4bd5b3068cafa88a~Yhu1eBIR20057100571epcas1p4I; Tue, 19 Nov 2019 09:40:23 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119094023epsmtrp1bfd0ccd2012991400c9d6abc4034ef8d~Yhu1c4V-T0080100801epsmtrp1n; Tue, 19 Nov 2019 09:40:23 +0000 (GMT) X-AuditID: b6c32a35-9bdff70000000fe8-50-5dd3b88706b6 Received: from epsmtip2.samsung.com ( [182.195.34.31]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 73.12.03654.788B3DD5; Tue, 19 Nov 2019 18:40:23 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20191119094023epsmtip25085d770e64fac0b2aa43d5460b0566e~Yhu1LhEgn0820508205epsmtip2C; Tue, 19 Nov 2019 09:40:23 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, Namjae Jeon Subject: [PATCH v3 06/13] exfat: add exfat entry operations Date: Tue, 19 Nov 2019 04:37:11 -0500 Message-Id: <20191119093718.3501-7-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119093718.3501-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFmpmk+LIzCtJLcpLzFFi42LZdlhTV7djx+VYg1+vGC0OP57EbtG8eD2b xcrVR5ksrt+9xWyxZ+9JFovLu+awWfyf9ZzV4sf0eost/46wWlx6/4HFgctj56y77B77565h 99h9s4HNo2/LKkaPzaerPT5vkvM4tP0Nm8ftZ9tYAjiicmwyUhNTUosUUvOS81My89JtlbyD 453jTc0MDHUNLS3MlRTyEnNTbZVcfAJ03TJzgE5UUihLzCkFCgUkFhcr6dvZFOWXlqQqZOQX l9gqpRak5BQYGhToFSfmFpfmpesl5+daGRoYGJkCVSbkZNxaYlrQFFVx/O8ZpgbGDrcuRk4O CQETiQ+3pjCB2EICOxglVt/m6mLkArI/MUp0vZvKDuF8Y5S4s+s0E0zHoZ9fmCESexkllv5p Z4Vr+bqvGSjDwcEmoC3xZ4soSIOIgL3E5tkHWEBqmAWOMEq8+DKBHSQhLGAtMfHXLzCbRUBV onHmXDYQmxcoPmfhORaIbfISqzccYAaxOQVsJB7OXsYIMkhC4AibxLae28wQRS4SB7f0sUPY whKvjm+BsqUkXva3sYMcJCFQLfFxP1R5B9AR320hbGOJm+s3sIKUMAtoSqzfpQ8RVpTY+Xsu I4jNLMAn8e5rDyvEFF6JjjYhiBJVib5Lh6FhIi3R1f4BaqmHxNS791khIdrPKHH0ltUERrlZ CAsWMDKuYhRLLSjOTU8tNiwwRI6uTYzgBKhluoNxyjmfQ4wCHIxKPLwK6pdjhVgTy4orcw8x SnAwK4nw+j26ECvEm5JYWZValB9fVJqTWnyI0RQYjhOZpUST84HJOa8k3tDUyNjY2MLEzNzM 1FhJnJfjx8VYIYH0xJLU7NTUgtQimD4mDk6pBsYFJRXxQlaXAnyt/BdlZuuI3l1fPe3cyveW wkX9HW5CzT06DPYCm/M4v9h9u8UYwfGlaebOz20CoqVrHikHb2LqSmlaZLh6duRFm/6Dy9Jk GOX0Vtt9/7ssYcL/q7/ijs25JVcdrWuts0dGWDx46+clBxe9VX3DzucR3Vx1VpDdOetmT5PE LyWW4oxEQy3mouJEAEtKLQ6WAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrHLMWRmVeSWpSXmKPExsWy7bCSvG77jsuxBtcna1kcfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9RZb/h1htbj0/gOLA5fHzll32T32z13D 7rH7ZgObR9+WVYwem09Xe3zeJOdxaPsbNo/bz7axBHBEcdmkpOZklqUW6dslcGXcWmJa0BRV cfzvGaYGxg63LkZODgkBE4lDP78wdzFycQgJ7GaUeDS9iQUiIS1x7MQZoAQHkC0scfhwMUTN B0aJDedXM4HE2QS0Jf5sEQUpFxFwlOjddZgFpIZZ4ByjxM5nyxhBEsIC1hITf/1iB7FZBFQl GmfOZQOxeYHicxaeg9olL7F6wwFmEJtTwEbi4WyIXiGgmsZHzewTGPkWMDKsYpRMLSjOTc8t NiwwzEst1ytOzC0uzUvXS87P3cQIDlUtzR2Ml5fEH2IU4GBU4uE9oXI5Vog1say4MvcQowQH s5IIr9+jC7FCvCmJlVWpRfnxRaU5qcWHGKU5WJTEeZ/mHYsUEkhPLEnNTk0tSC2CyTJxcEo1 MDZuOG+ofnZ2J0ex/m3tKKtLgl++u4i3uD7KZKh3vJK/ujDgzY+KMBfb/k2ReZX2C46tZefo nPC1k8V57YFraQrfvQz6Xh1Ic1DiNbbQ33r+993nB3dsSv+QJ+DgwHNh7woORdbjHqs6/qxY 5TrzaHIkc+yCCW7By/KqHpVcF5u3UjSqd8e2aiWW4oxEQy3mouJEAEg7okpRAgAA X-CMS-MailID: 20191119094023epcas1p4976d19f4b9a859ba4bd5b3068cafa88a X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119094023epcas1p4976d19f4b9a859ba4bd5b3068cafa88a References: <20191119093718.3501-1-namjae.jeon@samsung.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This adds the implementation of exfat entry operations for exfat. Signed-off-by: Namjae Jeon Signed-off-by: Sungjong Seo --- fs/exfat/fatent.c | 475 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 475 insertions(+) create mode 100644 fs/exfat/fatent.c diff --git a/fs/exfat/fatent.c b/fs/exfat/fatent.c new file mode 100644 index 000000000000..006c513ae5c0 --- /dev/null +++ b/fs/exfat/fatent.c @@ -0,0 +1,475 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "exfat_raw.h" +#include "exfat_fs.h" + +static int __exfat_ent_get(struct super_block *sb, unsigned int loc, + unsigned int *content) +{ + unsigned int off, _content; + sector_t sec; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bh; + + sec = sbi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2)); + off = (loc << 2) & (sb->s_blocksize - 1); + + bh = sb_bread(sb, sec); + if (!bh) + return -EIO; + + _content = le32_to_cpu(*(__le32 *)(&bh->b_data[off])); + + /* remap reserved clusters to simplify code */ + if (_content >= CLUSTER_32(0xFFFFFFF8)) + _content = EOF_CLUSTER; + + *content = CLUSTER_32(_content); + brelse(bh); + return 0; +} + +int exfat_ent_set(struct super_block *sb, unsigned int loc, + unsigned int content) +{ + unsigned int off; + sector_t sec; + __le32 *fat_entry; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bh; + + sec = sbi->FAT1_start_sector + (loc >> (sb->s_blocksize_bits-2)); + off = (loc << 2) & (sb->s_blocksize - 1); + + bh = sb_bread(sb, sec); + if (!bh) + return -EIO; + + fat_entry = (__le32 *)&(bh->b_data[off]); + *fat_entry = cpu_to_le32(content); + exfat_update_bh(sb, bh, sb->s_flags & SB_SYNCHRONOUS); + exfat_mirror_bh(sb, sec, bh); + brelse(bh); + return 0; +} + +static inline bool is_reserved_cluster(unsigned int clus) +{ + if (clus == FREE_CLUSTER || clus == EOF_CLUSTER || clus == BAD_CLUSTER) + return true; + return false; +} + +static inline bool is_valid_cluster(struct exfat_sb_info *sbi, + unsigned int clus) +{ + if (clus < BASE_CLUSTER || sbi->num_clusters <= clus) + return false; + return true; +} + +int exfat_ent_get(struct super_block *sb, unsigned int loc, + unsigned int *content) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + int err; + + if (!is_valid_cluster(sbi, loc)) { + exfat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", + loc); + return -EIO; + } + + err = __exfat_ent_get(sb, loc, content); + if (err) { + exfat_fs_error(sb, + "failed to access to FAT (entry 0x%08x, err:%d)", + loc, err); + return err; + } + + if (!is_reserved_cluster(*content) && + !is_valid_cluster(sbi, *content)) { + exfat_fs_error(sb, + "invalid access to FAT (entry 0x%08x) bogus content (0x%08x)", + loc, *content); + return -EIO; + } + + if (*content == FREE_CLUSTER) { + exfat_fs_error(sb, + "invalid access to FAT free cluster (entry 0x%08x)", + loc); + return -EIO; + } + + if (*content == BAD_CLUSTER) { + exfat_fs_error(sb, + "invalid access to FAT bad cluster (entry 0x%08x)", + loc); + return -EIO; + } + + return 0; +} + +int exfat_chain_cont_cluster(struct super_block *sb, unsigned int chain, + unsigned int len) +{ + if (!len) + return 0; + + while (len > 1) { + if (exfat_ent_set(sb, chain, chain + 1)) + return -EIO; + chain++; + len--; + } + + if (exfat_ent_set(sb, chain, EOF_CLUSTER)) + return -EIO; + return 0; +} + +int exfat_free_cluster(struct inode *inode, struct exfat_chain *p_chain) +{ + unsigned int num_clusters = 0; + unsigned int clu; + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + /* invalid cluster number */ + if (p_chain->dir == FREE_CLUSTER || p_chain->dir == EOF_CLUSTER) + return 0; + + /* no cluster to truncate */ + if (p_chain->size == 0) + return 0; + + /* check cluster validation */ + if (p_chain->dir < 2 && p_chain->dir >= sbi->num_clusters) { + exfat_msg(sb, KERN_ERR, "invalid start cluster (%u)", + p_chain->dir); + return -EIO; + } + + WRITE_ONCE(sbi->s_dirt, true); + clu = p_chain->dir; + + if (p_chain->flags == 0x03) { + do { + exfat_clear_bitmap(inode, clu-2); + clu++; + + num_clusters++; + } while (num_clusters < p_chain->size); + } else { + do { + exfat_clear_bitmap(inode, (clu - BASE_CLUSTER)); + + if (exfat_get_next_cluster(sb, &clu)) + goto out; + + num_clusters++; + } while (clu != EOF_CLUSTER); + } + +out: + sbi->used_clusters -= num_clusters; + return 0; +} + +int exfat_find_last_cluster(struct super_block *sb, struct exfat_chain *p_chain, + unsigned int *ret_clu) +{ + unsigned int clu, next; + unsigned int count = 0; + + next = p_chain->dir; + if (p_chain->flags == 0x03) { + *ret_clu = next + p_chain->size - 1; + return 0; + } + + do { + count++; + clu = next; + if (exfat_ent_get(sb, clu, &next)) + return -EIO; + } while (next != EOF_CLUSTER); + + if (p_chain->size != count) { + exfat_fs_error(sb, + "bogus directory size (clus : ondisk(%d) != counted(%d))", + p_chain->size, count); + return -EIO; + } + + *ret_clu = clu; + return 0; +} + +static inline int exfat_sync_bhs(struct buffer_head **bhs, int nr_bhs) +{ + int i, err = 0; + + for (i = 0; i < nr_bhs; i++) + write_dirty_buffer(bhs[i], 0); + + for (i = 0; i < nr_bhs; i++) { + wait_on_buffer(bhs[i]); + if (!err && !buffer_uptodate(bhs[i])) + err = -EIO; + } + return err; +} + +int exfat_zeroed_cluster(struct inode *dir, unsigned int clu) +{ + struct super_block *sb = dir->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bhs[MAX_BUF_PER_PAGE]; + int nr_bhs = MAX_BUF_PER_PAGE; + sector_t blknr, last_blknr; + int err, i, n; + + blknr = exfat_cluster_to_sector(sbi, clu); + last_blknr = blknr + sbi->sect_per_clus; + + if (last_blknr > sbi->num_sectors && sbi->num_sectors > 0) { + exfat_fs_error_ratelimit(sb, + "%s: out of range(sect:%llu len:%u)", + __func__, (unsigned long long)blknr, + sbi->sect_per_clus); + return -EIO; + } + + /* Zeroing the unused blocks on this cluster */ + n = 0; + while (blknr < last_blknr) { + bhs[n] = sb_getblk(sb, blknr); + if (!bhs[n]) { + err = -ENOMEM; + goto error; + } + memset(bhs[n]->b_data, 0, sb->s_blocksize); + exfat_update_bh(sb, bhs[n], 0); + + n++; + blknr++; + + if (n == nr_bhs) { + if (IS_DIRSYNC(dir)) { + err = exfat_sync_bhs(bhs, n); + if (err) + goto error; + } + + for (i = 0; i < n; i++) + brelse(bhs[i]); + n = 0; + } + } + + if (IS_DIRSYNC(dir)) { + err = exfat_sync_bhs(bhs, n); + if (err) + goto error; + } + + for (i = 0; i < n; i++) + brelse(bhs[i]); + + return 0; + +error: + exfat_msg(sb, KERN_ERR, "failed zeroed sect %llu\n", + (unsigned long long)blknr); + for (i = 0; i < n; i++) + bforget(bhs[i]); + + return err; +} + +int exfat_alloc_cluster(struct inode *inode, unsigned int num_alloc, + struct exfat_chain *p_chain) +{ + int ret = -ENOSPC; + unsigned int num_clusters = 0, total_cnt; + unsigned int hint_clu, new_clu, last_clu = EOF_CLUSTER; + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + total_cnt = sbi->num_clusters - BASE_CLUSTER; + + if (unlikely(total_cnt < sbi->used_clusters)) { + exfat_fs_error_ratelimit(sb, + "%s: invalid used clusters(t:%u,u:%u)\n", + __func__, total_cnt, sbi->used_clusters); + return -EIO; + } + + if (num_alloc > total_cnt - sbi->used_clusters) + return -ENOSPC; + + hint_clu = p_chain->dir; + /* find new cluster */ + if (hint_clu == EOF_CLUSTER) { + if (sbi->clu_srch_ptr < BASE_CLUSTER) { + exfat_msg(sb, KERN_ERR, + "sbi->clu_srch_ptr is invalid (%u)\n", + sbi->clu_srch_ptr); + sbi->clu_srch_ptr = BASE_CLUSTER; + } + + hint_clu = exfat_test_bitmap(sb, + sbi->clu_srch_ptr - BASE_CLUSTER); + if (hint_clu == EOF_CLUSTER) + return -ENOSPC; + } + + /* check cluster validation */ + if (hint_clu < BASE_CLUSTER && hint_clu >= sbi->num_clusters) { + exfat_msg(sb, KERN_ERR, "hint_cluster is invalid (%u)\n", + hint_clu); + hint_clu = BASE_CLUSTER; + if (p_chain->flags == 0x03) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, + num_clusters)) + return -EIO; + p_chain->flags = 0x01; + } + } + + WRITE_ONCE(sbi->s_dirt, true); + + p_chain->dir = EOF_CLUSTER; + + while ((new_clu = exfat_test_bitmap(sb, + hint_clu - BASE_CLUSTER)) != EOF_CLUSTER) { + if (new_clu != hint_clu && p_chain->flags == 0x03) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, + num_clusters)) { + ret = -EIO; + goto error; + } + p_chain->flags = 0x01; + } + + /* update allocation bitmap */ + if (exfat_set_bitmap(inode, new_clu - BASE_CLUSTER)) { + ret = -EIO; + goto error; + } + + num_clusters++; + + /* update FAT table */ + if (p_chain->flags == 0x01) { + if (exfat_ent_set(sb, new_clu, EOF_CLUSTER)) { + ret = -EIO; + goto error; + } + } + + if (p_chain->dir == EOF_CLUSTER) { + p_chain->dir = new_clu; + } else if (p_chain->flags == 0x01) { + if (exfat_ent_set(sb, last_clu, new_clu)) { + ret = -EIO; + goto error; + } + } + last_clu = new_clu; + + if (--num_alloc == 0) { + sbi->clu_srch_ptr = hint_clu; + sbi->used_clusters += num_clusters; + + p_chain->size += num_clusters; + return 0; + } + + hint_clu = new_clu + 1; + if (hint_clu >= sbi->num_clusters) { + hint_clu = BASE_CLUSTER; + + if (p_chain->flags == 0x03) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, + num_clusters)) { + ret = -EIO; + goto error; + } + p_chain->flags = 0x01; + } + } + } +error: + if (num_clusters) + exfat_free_cluster(inode, p_chain); + return ret; +} + +int exfat_count_num_clusters(struct super_block *sb, + struct exfat_chain *p_chain, unsigned int *ret_count) +{ + unsigned int i, count; + unsigned int clu; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + if (!p_chain->dir || p_chain->dir == EOF_CLUSTER) { + *ret_count = 0; + return 0; + } + + if (p_chain->flags == 0x03) { + *ret_count = p_chain->size; + return 0; + } + + clu = p_chain->dir; + count = 0; + for (i = BASE_CLUSTER; i < sbi->num_clusters; i++) { + count++; + if (exfat_ent_get(sb, clu, &clu)) + return -EIO; + if (clu == EOF_CLUSTER) + break; + } + + *ret_count = count; + return 0; +} + +int exfat_mirror_bh(struct super_block *sb, sector_t sec, + struct buffer_head *bh) +{ + struct buffer_head *c_bh; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + sector_t sec2; + int err = 0; + + if (sbi->FAT2_start_sector != sbi->FAT1_start_sector) { + sec2 = sec - sbi->FAT1_start_sector + sbi->FAT2_start_sector; + c_bh = sb_getblk(sb, sec2); + if (!c_bh) { + err = -ENOMEM; + goto out; + } + memcpy(c_bh->b_data, bh->b_data, sb->s_blocksize); + set_buffer_uptodate(c_bh); + mark_buffer_dirty(c_bh); + if (sb->s_flags & SB_SYNCHRONOUS) + err = sync_dirty_buffer(c_bh); + brelse(c_bh); + } +out: + return err; +} From patchwork Tue Nov 19 09:37:12 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11251637 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 93D436C1 for ; Tue, 19 Nov 2019 09:41:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 69B2A2230C for ; Tue, 19 Nov 2019 09:41:15 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="T7Bp0nrr" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727578AbfKSJk3 (ORCPT ); Tue, 19 Nov 2019 04:40:29 -0500 Received: from mailout3.samsung.com ([203.254.224.33]:28226 "EHLO mailout3.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726510AbfKSJk2 (ORCPT ); Tue, 19 Nov 2019 04:40:28 -0500 Received: from epcas1p3.samsung.com (unknown [182.195.41.47]) by mailout3.samsung.com (KnoxPortal) with ESMTP id 20191119094026epoutp03e63fdd2e6179eb94f7587ebb8a5138da~Yhu3i3q_d1901319013epoutp03K for ; Tue, 19 Nov 2019 09:40:26 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout3.samsung.com 20191119094026epoutp03e63fdd2e6179eb94f7587ebb8a5138da~Yhu3i3q_d1901319013epoutp03K DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574156426; bh=iRjw2RD0asFmnqKKqPfk/KOVSDaCUefdsIbM8/AfuQo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=T7Bp0nrr7hZAl4GWU8rkN8GmNdaLz+0dz5MIS7XBkGHczyO8fyvi2XGZ/fHQ3xgRL YOG95yHfvMNa2DyO3ulH5ivzNaOP6XVmtpW6asQg19fIwcb1P1wPDiAY5E4y5x3exc KWpibvkXZfUEy6i3XTwWZ2VhRZFK3V8cAelMYEwY= Received: from epsnrtp1.localdomain (unknown [182.195.42.162]) by epcas1p4.samsung.com (KnoxPortal) with ESMTP id 20191119094025epcas1p42cbfeb77c5bbd97ea5a3d5000d41d203~Yhu3GX8BB0056700567epcas1p4i; Tue, 19 Nov 2019 09:40:25 +0000 (GMT) Received: from epsmges1p3.samsung.com (unknown [182.195.40.162]) by epsnrtp1.localdomain (Postfix) with ESMTP id 47HLQw4ms5zMqYls; Tue, 19 Nov 2019 09:40:24 +0000 (GMT) Received: from epcas1p1.samsung.com ( [182.195.41.45]) by epsmges1p3.samsung.com (Symantec Messaging Gateway) with SMTP id F5.07.04080.888B3DD5; Tue, 19 Nov 2019 18:40:24 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p3.samsung.com (KnoxPortal) with ESMTPA id 20191119094024epcas1p3991c5d94c2cf40388d64c0bd9c30092d~Yhu18H9J41136411364epcas1p3Q; Tue, 19 Nov 2019 09:40:24 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119094024epsmtrp13bb86cbc7e8295cbc47f0f2dc516fe02~Yhu17VuUH0080100801epsmtrp1o; Tue, 19 Nov 2019 09:40:24 +0000 (GMT) X-AuditID: b6c32a37-7b5ff70000000ff0-b6-5dd3b888b195 Received: from epsmtip2.samsung.com ( [182.195.34.31]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 24.12.03654.888B3DD5; Tue, 19 Nov 2019 18:40:24 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20191119094024epsmtip276d03d903383562cbb05b922b592d982~Yhu1vRChc0820508205epsmtip2D; Tue, 19 Nov 2019 09:40:24 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, Namjae Jeon Subject: [PATCH v3 07/13] exfat: add bitmap operations Date: Tue, 19 Nov 2019 04:37:12 -0500 Message-Id: <20191119093718.3501-8-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119093718.3501-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA01SWUwTURTN6ywdiNVJVXxWo2UCMaItrbXtqKAmEpwoMU38QROCIx0p2mUy 0yLKh7ghEgXlQxJANNG4FFkUwuIGKatLDFKNK3EhbELcF4gG7TBuf+eee867J/ddAlHvxTVE htvLCW7WSeHhaH3rQp0urzGYYih/Hkm39hUp6f1nqnH6YkW7gn7U+xShr9+4hdLBq2U4/bNk EKPHivfQdRNtGN3z7j26OpxpKulVMs0nLymZa09ycKagzg+Y2jvZzKcr85hAwyjOPBuoR23E Zmecg2PtnKDl3Gkee4Y7PZ5avzF1TarZYjDqjMtoK6V1sy4unkpIsukSM5yhiJQ2k3X6QpSN FUUqdmWc4PF5Oa3DI3rjKY63O3mjgdeLrEv0udP1aR7XcqPBsMQcUm5xOoreRfGHErI6/Y4c 0GHKB2EEJJfCsv5KLB+EE2qyEcCG7su4XHwE8MuJEiAXXwHsPtAe6hCTlryqjZJbTd4A8Oi4 76+h8FstkDQ4uQj+qJspaWaQq2BtaQsqaRCyDcChz8eUUmM6aYHDE12ohFEyGg75ezAJq8gV 8GzHQ6Ucbz6sqGlBJBxGxsFXpeeAzHfhcLTTKuMEWFndgsl4OnzTWffbq4HDhblKOXM2/NCM yHReKMO3eBmb4JPqGkySIORCWH01VqYjYdP3k5OTEHIqfPvlCCa/ooJ5uWpZEg0LeloVMp4D 8w+9/z2UgdeKgwp5I4UA3r3wADkG5pX8m3AaAD+I4HjRlc6JRt70/2ddAZPnF2NtBDX3kgKA JAA1RaVdEExRY2ymuMsVAJBAqBmqDa+7U9QqO7trNyd4UgWfkxMDwBza43FEMzPNEzpmtzfV aF5iMpnopRarxWyiZqmIsfspajKd9XI7OI7nhD8+BRGmyQHTdD1Rva8X/ayq8vdr9P5tiS8P Lz74QkvN3TyuDyZoz+xHda2VQnd5YC1fu28w+2bpbEvxbiIied0FtWfnglOG7eP+jg/4G81I 1qbBga22XPsEQyUP6ysJ7r7tNriH8X0R1pFyNjbubFvfcnRFTeltxVjjU+Ex++m81Z5c0Emh ooM1xiCCyP4C1emoxpQDAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrDLMWRmVeSWpSXmKPExsWy7bCSvG7HjsuxBtO281gcfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9RZb/h1htbj0/gOLA5fHzll32T32z13D 7rH7ZgObR9+WVYwem09Xe3zeJOdxaPsbNo/bz7axBHBEcdmkpOZklqUW6dslcGVMeq9S0O5S cXxVRgPjMeMuRg4OCQETiY51wV2MXBxCArsZJf5P+sbWxcgJFJeWOHbiDDNEjbDE4cPFEDUf GCV2HJ7NAhJnE9CW+LNFFKRcRMBRonfXYRaQGmaBc4wSO58tYwRJCAuYSbz8d4IFxGYRUJV4 seoSK4jNK2AtseTYVXaIXfISqzccYAaxOQVsJB7OhugVAqppfNTMPoGRbwEjwypGydSC4tz0 3GLDAsO81HK94sTc4tK8dL3k/NxNjOAw1dLcwXh5SfwhRgEORiUe3hMql2OFWBPLiitzDzFK cDArifD6PboQK8SbklhZlVqUH19UmpNafIhRmoNFSZz3ad6xSCGB9MSS1OzU1ILUIpgsEwen VAMjw47ARoFfn97/9RF5FFc7o5GlcQJDjCuzoMF/Py8vC+6pjacV3fsk3ltueKW+YEkH46O5 b+Tsjv7W21WtIxsyxUojUex964scXje3B7VP1r2fkGD7SXTeyVf6er0HUh7WTvmZXrnjLZPU nd2rOjezevzW8Nseournbb/GTygjpXCyvfqWifpKLMUZiYZazEXFiQCzaXQBTwIAAA== X-CMS-MailID: 20191119094024epcas1p3991c5d94c2cf40388d64c0bd9c30092d X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119094024epcas1p3991c5d94c2cf40388d64c0bd9c30092d References: <20191119093718.3501-1-namjae.jeon@samsung.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This adds the implementation of bitmap operations for exfat. Signed-off-by: Namjae Jeon Signed-off-by: Sungjong Seo --- fs/exfat/balloc.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 fs/exfat/balloc.c diff --git a/fs/exfat/balloc.c b/fs/exfat/balloc.c new file mode 100644 index 000000000000..930e0ea3dbfd --- /dev/null +++ b/fs/exfat/balloc.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "exfat_raw.h" +#include "exfat_fs.h" + +static const unsigned char free_bit[] = { + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/* 0 ~ 19*/ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3,/* 20 ~ 39*/ + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/* 40 ~ 59*/ + 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/* 60 ~ 79*/ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2,/* 80 ~ 99*/ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3,/*100 ~ 119*/ + 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*120 ~ 139*/ + 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,/*140 ~ 159*/ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/*160 ~ 179*/ + 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3,/*180 ~ 199*/ + 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*200 ~ 219*/ + 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/*220 ~ 239*/ + 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /*240 ~ 254*/ +}; + +static const unsigned char used_bit[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3,/* 0 ~ 19*/ + 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4,/* 20 ~ 39*/ + 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,/* 40 ~ 59*/ + 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,/* 60 ~ 79*/ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,/* 80 ~ 99*/ + 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,/*100 ~ 119*/ + 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,/*120 ~ 139*/ + 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,/*140 ~ 159*/ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,/*160 ~ 179*/ + 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,/*180 ~ 199*/ + 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6,/*200 ~ 219*/ + 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,/*220 ~ 239*/ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 /*240 ~ 255*/ +}; + +/* + * Allocation Bitmap Management Functions + */ +static int exfat_allocate_bitmap(struct super_block *sb, + struct exfat_dentry *ep) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + long long map_size; + unsigned int i, need_map_size; + sector_t sector; + + sbi->map_clu = le32_to_cpu(ep->bitmap_start_clu); + map_size = le64_to_cpu(ep->bitmap_size); + need_map_size = (((sbi->num_clusters - BASE_CLUSTER) - 1) >> 3) + 1; + if (need_map_size != map_size) { + exfat_msg(sb, KERN_ERR, + "bogus allocation bitmap size(need : %u, cur : %lld)", + need_map_size, map_size); + /* + * Only allowed when bogus allocation + * bitmap size is large + */ + if (need_map_size > map_size) + return -EIO; + } + sbi->map_sectors = ((need_map_size - 1) >> + (sb->s_blocksize_bits)) + 1; + sbi->vol_amap = kmalloc_array(sbi->map_sectors, + sizeof(struct buffer_head *), GFP_KERNEL); + if (!sbi->vol_amap) + return -ENOMEM; + + sector = exfat_cluster_to_sector(sbi, sbi->map_clu); + for (i = 0; i < sbi->map_sectors; i++) { + sbi->vol_amap[i] = sb_bread(sb, sector + i); + if (!sbi->vol_amap[i]) { + /* release all buffers and free vol_amap */ + int j = 0; + + while (j < i) + brelse(sbi->vol_amap[j++]); + + kfree(sbi->vol_amap); + sbi->vol_amap = NULL; + return -EIO; + } + } + + sbi->pbr_bh = NULL; + return 0; +} + +int exfat_load_bitmap(struct super_block *sb) +{ + unsigned int i, type; + struct exfat_chain clu; + struct exfat_dentry *ep = NULL; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bh; + + exfat_chain_set(&clu, sbi->root_dir, 0, 0x01); + + while (clu.dir != EOF_CLUSTER) { + for (i = 0; i < sbi->dentries_per_clu; i++) { + ep = exfat_get_dentry(sb, &clu, i, &bh, NULL); + if (!ep) + return -EIO; + + type = exfat_get_entry_type(ep); + if (type == TYPE_UNUSED) + break; + if (type != TYPE_BITMAP) + continue; + if (ep->bitmap_flags == 0x0) { + int err; + + err = exfat_allocate_bitmap(sb, ep); + brelse(bh); + return err; + } + } + + if (exfat_get_next_cluster(sb, &clu.dir)) + return -EIO; + } + + return -EINVAL; +} + +void exfat_free_bitmap(struct super_block *sb) +{ + int i; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + brelse(sbi->pbr_bh); + + for (i = 0; i < sbi->map_sectors; i++) + __brelse(sbi->vol_amap[i]); + + kfree(sbi->vol_amap); + sbi->vol_amap = NULL; +} + +/* + * If the value of "clu" is 0, it means cluster 2 which is the first cluster of + * the cluster heap. + */ +int exfat_set_bitmap(struct inode *inode, unsigned int clu) +{ + int i, b; + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + i = clu >> (sb->s_blocksize_bits + 3); + b = clu & ((sb->s_blocksize << 3) - 1); + + set_bit_le(b, sbi->vol_amap[i]->b_data); + exfat_update_bh(sb, sbi->vol_amap[i], IS_DIRSYNC(inode)); + + return 0; +} + +/* + * If the value of "clu" is 0, it means cluster 2 which is the first cluster of + * the cluster heap. + */ +void exfat_clear_bitmap(struct inode *inode, unsigned int clu) +{ + int i, b; + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct exfat_mount_options *opts = &sbi->options; + + i = clu >> (sb->s_blocksize_bits + 3); + b = clu & ((sb->s_blocksize << 3) - 1); + + clear_bit_le(b, sbi->vol_amap[i]->b_data); + exfat_update_bh(sb, sbi->vol_amap[i], IS_DIRSYNC(inode)); + + if (opts->discard) { + int ret_discard; + + ret_discard = sb_issue_discard(sb, + exfat_cluster_to_sector(sbi, clu + 2), + (1 << sbi->sect_per_clus_bits), GFP_NOFS, 0); + + if (ret_discard == -EOPNOTSUPP) { + exfat_msg(sb, KERN_ERR, + "discard not supported by device, disabling"); + opts->discard = 0; + } + } +} + +/* + * If the value of "clu" is 0, it means cluster 2 which is the first cluster of + * the cluster heap. + */ +unsigned int exfat_test_bitmap(struct super_block *sb, unsigned int clu) +{ + unsigned int i, map_i, map_b; + unsigned int clu_base, clu_free; + unsigned char k, clu_mask; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + clu_base = (clu & ~(0x7)) + 2; + clu_mask = (1 << (clu - clu_base + 2)) - 1; + + map_i = clu >> (sb->s_blocksize_bits + 3); + map_b = (clu >> 3) & (unsigned int)(sb->s_blocksize - 1); + + for (i = 2; i < sbi->num_clusters; i += 8) { + k = *(sbi->vol_amap[map_i]->b_data + map_b); + if (clu_mask > 0) { + k |= clu_mask; + clu_mask = 0; + } + if (k < 0xFF) { + clu_free = clu_base + free_bit[k]; + if (clu_free < sbi->num_clusters) + return clu_free; + } + clu_base += 8; + + if (++map_b >= sb->s_blocksize || + clu_base >= sbi->num_clusters) { + if (++map_i >= sbi->map_sectors) { + clu_base = 2; + map_i = 0; + } + map_b = 0; + } + } + + return EOF_CLUSTER; +} + +int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + unsigned int count = 0; + unsigned int i, map_i = 0, map_b = 0; + unsigned int total_clus = sbi->num_clusters - 2; + unsigned int last_mask = total_clus & 7; + unsigned char clu_bits; + const unsigned char last_bit_mask[] = {0, 0b00000001, 0b00000011, + 0b00000111, 0b00001111, 0b00011111, 0b00111111, 0b01111111}; + + total_clus &= ~last_mask; + for (i = 0; i < total_clus; i += 8) { + clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b); + count += used_bit[clu_bits]; + if (++map_b >= (unsigned int)sb->s_blocksize) { + map_i++; + map_b = 0; + } + } + + if (last_mask) { + clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b); + clu_bits &= last_bit_mask[last_mask]; + count += used_bit[clu_bits]; + } + + *ret_count = count; + return 0; +} From patchwork Tue Nov 19 09:37:13 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11251631 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 25B776C1 for ; Tue, 19 Nov 2019 09:40:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id F045422311 for ; Tue, 19 Nov 2019 09:40:57 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="uXEbTwz+" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727738AbfKSJk5 (ORCPT ); Tue, 19 Nov 2019 04:40:57 -0500 Received: from mailout2.samsung.com ([203.254.224.25]:40701 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727539AbfKSJkb (ORCPT ); Tue, 19 Nov 2019 04:40:31 -0500 Received: from epcas1p3.samsung.com (unknown [182.195.41.47]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20191119094027epoutp0287ff90a6252b790069af705bbec7070c~Yhu4c_k650391103911epoutp02J for ; Tue, 19 Nov 2019 09:40:27 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20191119094027epoutp0287ff90a6252b790069af705bbec7070c~Yhu4c_k650391103911epoutp02J DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574156427; bh=cMpYo+3xAlzGcb/z0sUvvooDr48c4npcG1qhTtMBMh8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uXEbTwz+cpPwKvuT3+nbSMwPK/TzL1+0Hu6eN8UwTmgwMHN/eQGCnqq07cb+GNb1U PjgEBNZYzZFzEjyKDU/Q1CPE6SIrWxqKwPv9DdxGxShxBmflx/BwBSHGNGOEm8KfmX q05WnyKD4x2Y+19ef/vJxOKuzre0WIjxnMHnqxyc= Received: from epsnrtp4.localdomain (unknown [182.195.42.165]) by epcas1p3.samsung.com (KnoxPortal) with ESMTP id 20191119094026epcas1p346b1d6ce9120519ffb2a53f933c9899d~Yhu4CM4kz1135011350epcas1p3e; Tue, 19 Nov 2019 09:40:26 +0000 (GMT) Received: from epsmges1p3.samsung.com (unknown [182.195.40.162]) by epsnrtp4.localdomain (Postfix) with ESMTP id 47HLQx4nKmzMqYkZ; Tue, 19 Nov 2019 09:40:25 +0000 (GMT) Received: from epcas1p4.samsung.com ( [182.195.41.48]) by epsmges1p3.samsung.com (Symantec Messaging Gateway) with SMTP id 27.07.04080.988B3DD5; Tue, 19 Nov 2019 18:40:25 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p1.samsung.com (KnoxPortal) with ESMTPA id 20191119094024epcas1p1be385d521ef64ae0e62da3f6f9bf3401~Yhu2f5N9e1202912029epcas1p1g; Tue, 19 Nov 2019 09:40:24 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119094024epsmtrp1899114b5308c95db1f0a069dd5f8cc50~Yhu2fPgYq0080100801epsmtrp1p; Tue, 19 Nov 2019 09:40:24 +0000 (GMT) X-AuditID: b6c32a37-7cdff70000000ff0-b9-5dd3b889c4f2 Received: from epsmtip2.samsung.com ( [182.195.34.31]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id D4.12.03654.888B3DD5; Tue, 19 Nov 2019 18:40:24 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20191119094024epsmtip2c47c766629c77fc1487f5b0a56cec03f~Yhu2QuNXT0708907089epsmtip2F; Tue, 19 Nov 2019 09:40:24 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, Namjae Jeon Subject: [PATCH v3 08/13] exfat: add exfat cache Date: Tue, 19 Nov 2019 04:37:13 -0500 Message-Id: <20191119093718.3501-9-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119093718.3501-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA02SbUhTYRTHeXa3e+/MxWVaPVjYuhClMd2a05u5DJS6lYVQX3pZdtPLJu2t 3S0yP2iZL4lpRSFolpFLdJqWw5kmivZKKKlQqQi9QGqGaVoapbXtzurb7znnf87/cJ6DI9Jc NARPN9lYq4kxkGiAsLk7TCG/0DKgVdy6Kqa6P1zBqJzbDShV43wsoF6PDCHUw/bnQmqg9TpK /S4bFVHzpVmUa/GRiOr/MiXcHkA/KBvB6I6KOoxuG8xG6WJXLaCbXmTSM/dD6S73Z5Qe/tgs TMYPGeL0LJPGWmWsKdWclm7Sacg9+1MSUtTRCqVcuYWKIWUmxshqyMSkZPmOdINnRFJ2ijHY PaFkhuPIyG1xVrPdxsr0Zs6mIVlLmsGiVFgiOMbI2U26iFSzMVapUGxWe5THDPq3+dMiS96O 0+23eoXZwEkVAjEOiShYPVYq8LKUaAFwok9XCAI8/BXAkeHrAv7xHcDKiz3oUkVF80WET7QD WFU8Lvxb4rzS5sngOEpsgr9cK7wFwUQ8bCrv9GkQ4hGAY7OXMG8iiFDAzsfDPm8hsR4uujt8 DhJiKzybe9nvthY6GzsRL4uJOPiu/A7wNoLEMxTWj834RYkwv86J8BwEPz11YTyHwPGSPMw7 ECQy4XSHX1LgGWJOw7MKDjY0irwShAiDDa2RfHgdfPCzAngZIZbDyW9FIr6LBBbkSXnJeljc 3y3geTUszJ/ym9LQfXMC5VdSAuAbxzXsEggt++dQCUAtWMlaOKOO5ZQW1f8fdh/4TjA8pgU0 9iZ1AQIHZKBEtmFAKxUxp7gMYxeAOEIGS/a9f6mVStKYjDOs1ZxitRtYrguoPYu8jISsSDV7 DtpkS1GqN6tUKioqOiZarSJXSfD5Pq2U0DE29gTLWljrUp0AF4dkg+CNbw4yx3sLb4Qye+fh vcOOkiPlYmvH+2UOMrZgQB7RIw2cvJ1T/Kq1YfTJlODkwhMytUUTX5rwhdYsuM5jy23Td7Eg y5rBIkltTslk5RA2uyvzrSOrZ+cBu+1DkLmp+lx9Dng3xzCJM3NPJxLjPjtq3Eevlckjwnd3 66t+bCOFnJ5RhiNWjvkDgjobe5gDAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrPLMWRmVeSWpSXmKPExsWy7bCSvG7HjsuxBrP+6FkcfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9RZb/h1htbj0/gOLA5fHzll32T32z13D 7rH7ZgObR9+WVYwem09Xe3zeJOdxaPsbNo/bz7axBHBEcdmkpOZklqUW6dslcGU8aP/IWtDm VrF34TmWBsbVFl2MnBwSAiYSc7f1MncxcnEICexmlLhx9icjREJa4tiJM0AJDiBbWOLw4WKI mg+MEs8vPGQHibMJaEv82SIKUi4i4CjRu+swC0gNs8A5Romdz5aBzREWMJA4cPQ2E4jNIqAq 8W/7fjYQm1fAWqKxdSIbxC55idUbDjCD2JwCNhIPZ0P0CoHUPGpmn8DIt4CRYRWjZGpBcW56 brFhgWFearlecWJucWleul5yfu4mRnCwamnuYLy8JP4QowAHoxIP7wmVy7FCrIllxZW5hxgl OJiVRHj9Hl2IFeJNSaysSi3Kjy8qzUktPsQozcGiJM77NO9YpJBAemJJanZqakFqEUyWiYNT qoExQaYiyl1YwqY6q0S6S/j/xokLr5ad0zzcska1P7HwSa69ct8v9sVLXltPbrqg9nfL28hZ nH3H4tpt7VV3h54LePNTd7ZNu+FCq7sTJnb92b1SZe8r/Q1rZ5j4ht5Rby6R3Lx7zsbHNiwt +Sd+3MypPriwIWxT3indxU8fzJJz76zeckTVbluxEktxRqKhFnNRcSIA3dBl3FICAAA= X-CMS-MailID: 20191119094024epcas1p1be385d521ef64ae0e62da3f6f9bf3401 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119094024epcas1p1be385d521ef64ae0e62da3f6f9bf3401 References: <20191119093718.3501-1-namjae.jeon@samsung.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This adds the implementation of exfat cache. Signed-off-by: Namjae Jeon Signed-off-by: Sungjong Seo --- fs/exfat/cache.c | 325 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100644 fs/exfat/cache.c diff --git a/fs/exfat/cache.c b/fs/exfat/cache.c new file mode 100644 index 000000000000..b87438360103 --- /dev/null +++ b/fs/exfat/cache.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * linux/fs/fat/cache.c + * + * Written 1992,1993 by Werner Almesberger + * + * Mar 1999. AV. Changed cache, so that it uses the starting cluster instead + * of inode number. + * May 1999. AV. Fixed the bogosity with FAT32 (read "FAT28"). Fscking lusers. + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include + +#include "exfat_raw.h" +#include "exfat_fs.h" + +#define EXFAT_CACHE_VALID 0 +#define EXFAT_MAX_CACHE 16 + +struct exfat_cache { + struct list_head cache_list; + unsigned int nr_contig; /* number of contiguous clusters */ + unsigned int fcluster; /* cluster number in the file. */ + unsigned int dcluster; /* cluster number on disk. */ +}; + +struct exfat_cache_id { + unsigned int id; + unsigned int nr_contig; + unsigned int fcluster; + unsigned int dcluster; +}; + +static struct kmem_cache *exfat_cachep; + +static void exfat_cache_init_once(void *c) +{ + struct exfat_cache *cache = (struct exfat_cache *)c; + + INIT_LIST_HEAD(&cache->cache_list); +} + +int exfat_cache_init(void) +{ + exfat_cachep = kmem_cache_create("exfat_cache", + sizeof(struct exfat_cache), + 0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD, + exfat_cache_init_once); + if (!exfat_cachep) + return -ENOMEM; + return 0; +} + +void exfat_cache_shutdown(void) +{ + if (!exfat_cachep) + return; + kmem_cache_destroy(exfat_cachep); +} + +void exfat_cache_init_inode(struct inode *inode) +{ + struct exfat_inode_info *ei = EXFAT_I(inode); + + spin_lock_init(&ei->cache_lru_lock); + ei->nr_caches = 0; + ei->cache_valid_id = EXFAT_CACHE_VALID + 1; + INIT_LIST_HEAD(&ei->cache_lru); +} + +static inline struct exfat_cache *exfat_cache_alloc(void) +{ + return kmem_cache_alloc(exfat_cachep, GFP_NOFS); +} + +static inline void exfat_cache_free(struct exfat_cache *cache) +{ + WARN_ON(!list_empty(&cache->cache_list)); + kmem_cache_free(exfat_cachep, cache); +} + +static inline void exfat_cache_update_lru(struct inode *inode, + struct exfat_cache *cache) +{ + struct exfat_inode_info *ei = EXFAT_I(inode); + + if (ei->cache_lru.next != &cache->cache_list) + list_move(&cache->cache_list, &ei->cache_lru); +} + +static unsigned int exfat_cache_lookup(struct inode *inode, + unsigned int fclus, struct exfat_cache_id *cid, + unsigned int *cached_fclus, unsigned int *cached_dclus) +{ + struct exfat_inode_info *ei = EXFAT_I(inode); + static struct exfat_cache nohit = { .fcluster = 0, }; + struct exfat_cache *hit = &nohit, *p; + unsigned int offset = EOF_CLUSTER; + + spin_lock(&ei->cache_lru_lock); + list_for_each_entry(p, &ei->cache_lru, cache_list) { + /* Find the cache of "fclus" or nearest cache. */ + if (p->fcluster <= fclus && hit->fcluster < p->fcluster) { + hit = p; + if (hit->fcluster + hit->nr_contig < fclus) { + offset = hit->nr_contig; + } else { + offset = fclus - hit->fcluster; + break; + } + } + } + if (hit != &nohit) { + exfat_cache_update_lru(inode, hit); + + cid->id = ei->cache_valid_id; + cid->nr_contig = hit->nr_contig; + cid->fcluster = hit->fcluster; + cid->dcluster = hit->dcluster; + *cached_fclus = cid->fcluster + offset; + *cached_dclus = cid->dcluster + offset; + } + spin_unlock(&ei->cache_lru_lock); + + return offset; +} + +static struct exfat_cache *exfat_cache_merge(struct inode *inode, + struct exfat_cache_id *new) +{ + struct exfat_inode_info *ei = EXFAT_I(inode); + struct exfat_cache *p; + + list_for_each_entry(p, &ei->cache_lru, cache_list) { + /* Find the same part as "new" in cluster-chain. */ + if (p->fcluster == new->fcluster) { + if (new->nr_contig > p->nr_contig) + p->nr_contig = new->nr_contig; + return p; + } + } + return NULL; +} + +static void exfat_cache_add(struct inode *inode, + struct exfat_cache_id *new) +{ + struct exfat_inode_info *ei = EXFAT_I(inode); + struct exfat_cache *cache, *tmp; + + if (new->fcluster == EOF_CLUSTER) /* dummy cache */ + return; + + spin_lock(&ei->cache_lru_lock); + if (new->id != EXFAT_CACHE_VALID && + new->id != ei->cache_valid_id) + goto out; /* this cache was invalidated */ + + cache = exfat_cache_merge(inode, new); + if (cache == NULL) { + if (ei->nr_caches < EXFAT_MAX_CACHE) { + ei->nr_caches++; + spin_unlock(&ei->cache_lru_lock); + + tmp = exfat_cache_alloc(); + if (!tmp) { + spin_lock(&ei->cache_lru_lock); + ei->nr_caches--; + spin_unlock(&ei->cache_lru_lock); + return; + } + + spin_lock(&ei->cache_lru_lock); + cache = exfat_cache_merge(inode, new); + if (cache != NULL) { + ei->nr_caches--; + exfat_cache_free(tmp); + goto out_update_lru; + } + cache = tmp; + } else { + struct list_head *p = ei->cache_lru.prev; + + cache = list_entry(p, + struct exfat_cache, cache_list); + } + cache->fcluster = new->fcluster; + cache->dcluster = new->dcluster; + cache->nr_contig = new->nr_contig; + } +out_update_lru: + exfat_cache_update_lru(inode, cache); +out: + spin_unlock(&ei->cache_lru_lock); +} + +/* + * Cache invalidation occurs rarely, thus the LRU chain is not updated. It + * fixes itself after a while. + */ +static void __exfat_cache_inval_inode(struct inode *inode) +{ + struct exfat_inode_info *ei = EXFAT_I(inode); + struct exfat_cache *cache; + + while (!list_empty(&ei->cache_lru)) { + cache = list_entry(ei->cache_lru.next, + struct exfat_cache, cache_list); + list_del_init(&cache->cache_list); + ei->nr_caches--; + exfat_cache_free(cache); + } + /* Update. The copy of caches before this id is discarded. */ + ei->cache_valid_id++; + if (ei->cache_valid_id == EXFAT_CACHE_VALID) + ei->cache_valid_id++; +} + +void exfat_cache_inval_inode(struct inode *inode) +{ + struct exfat_inode_info *ei = EXFAT_I(inode); + + spin_lock(&ei->cache_lru_lock); + __exfat_cache_inval_inode(inode); + spin_unlock(&ei->cache_lru_lock); +} + +static inline int cache_contiguous(struct exfat_cache_id *cid, + unsigned int dclus) +{ + cid->nr_contig++; + return cid->dcluster + cid->nr_contig == dclus; +} + +static inline void cache_init(struct exfat_cache_id *cid, + unsigned int fclus, unsigned int dclus) +{ + cid->id = EXFAT_CACHE_VALID; + cid->fcluster = fclus; + cid->dcluster = dclus; + cid->nr_contig = 0; +} + +int exfat_get_cluster(struct inode *inode, unsigned int cluster, + unsigned int *fclus, unsigned int *dclus, + unsigned int *last_dclus, int allow_eof) +{ + struct super_block *sb = inode->i_sb; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + unsigned int limit = sbi->num_clusters; + struct exfat_inode_info *ei = EXFAT_I(inode); + struct exfat_cache_id cid; + unsigned int content; + + if (ei->start_clu == FREE_CLUSTER) { + exfat_fs_error(sb, + "invalid access to exfat cache (entry 0x%08x)", + ei->start_clu); + return -EIO; + } + + *fclus = 0; + *dclus = ei->start_clu; + *last_dclus = *dclus; + + /* + * Don`t use exfat_cache if zero offset or non-cluster allocation + */ + if (cluster == 0 || *dclus == EOF_CLUSTER) + return 0; + + cache_init(&cid, EOF_CLUSTER, EOF_CLUSTER); + + if (exfat_cache_lookup(inode, cluster, &cid, fclus, dclus) == + EOF_CLUSTER) { + /* + * dummy, always not contiguous + * This is reinitialized by cache_init(), later. + */ + WARN_ON(cid.id != EXFAT_CACHE_VALID || + cid.fcluster != EOF_CLUSTER || + cid.dcluster != EOF_CLUSTER || + cid.nr_contig != 0); + } + + if (*fclus == cluster) + return 0; + + while (*fclus < cluster) { + /* prevent the infinite loop of cluster chain */ + if (*fclus > limit) { + exfat_fs_error(sb, + "detected the cluster chain loop (i_pos %u)", + (*fclus)); + return -EIO; + } + + if (exfat_ent_get(sb, *dclus, &content)) + return -EIO; + + *last_dclus = *dclus; + *dclus = content; + (*fclus)++; + + if (content == EOF_CLUSTER) { + if (!allow_eof) { + exfat_fs_error(sb, + "invalid cluster chain (i_pos %u, last_clus 0x%08x is EOF)", + *fclus, (*last_dclus)); + return -EIO; + } + + break; + } + + if (!cache_contiguous(&cid, *dclus)) + cache_init(&cid, *fclus, *dclus); + } + + exfat_cache_add(inode, &cid); + return 0; +} From patchwork Tue Nov 19 09:37:14 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11251629 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0911E6C1 for ; Tue, 19 Nov 2019 09:40:57 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D337A22311 for ; Tue, 19 Nov 2019 09:40:56 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="j0k8ymTB" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727629AbfKSJkb (ORCPT ); Tue, 19 Nov 2019 04:40:31 -0500 Received: from mailout3.samsung.com ([203.254.224.33]:28226 "EHLO mailout3.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726510AbfKSJkb (ORCPT ); Tue, 19 Nov 2019 04:40:31 -0500 Received: from epcas1p2.samsung.com (unknown [182.195.41.46]) by mailout3.samsung.com (KnoxPortal) with ESMTP id 20191119094029epoutp033fe3b2aa201e28b794f3004c6d54d70a~Yhu6ky0zi1901719017epoutp03L for ; Tue, 19 Nov 2019 09:40:29 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout3.samsung.com 20191119094029epoutp033fe3b2aa201e28b794f3004c6d54d70a~Yhu6ky0zi1901719017epoutp03L DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574156429; bh=XpygHjuf8d7CE3zc20VcrTtJAW5giJFyIx169VoIE/Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=j0k8ymTBt3aQWFU9gjypZzZ4G1hzVt3NMJOoofGbNfP9ZwsEv4mIaWTk1AXWiJPcG m+FzbuUYKNguGEkS3Y+yE7ZUdB2pzTjKYUE69sbNqIQ2bEybTAP6r2nCV+k3AT5lx6 HZ5r4mViGYire+bB1BFpbeHCUgVyfWsrjMq6uHaY= Received: from epsnrtp2.localdomain (unknown [182.195.42.163]) by epcas1p2.samsung.com (KnoxPortal) with ESMTP id 20191119094028epcas1p228282a1ee2c2b55f649efbc7f574d07c~Yhu5tjGWV0818108181epcas1p2N; Tue, 19 Nov 2019 09:40:28 +0000 (GMT) Received: from epsmges1p5.samsung.com (unknown [182.195.40.165]) by epsnrtp2.localdomain (Postfix) with ESMTP id 47HLQz35fRzMqYkg; Tue, 19 Nov 2019 09:40:27 +0000 (GMT) Received: from epcas1p2.samsung.com ( [182.195.41.46]) by epsmges1p5.samsung.com (Symantec Messaging Gateway) with SMTP id 8F.BF.04237.A88B3DD5; Tue, 19 Nov 2019 18:40:26 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p2.samsung.com (KnoxPortal) with ESMTPA id 20191119094025epcas1p2bbb3121ac9f1d427f09f6548b8d35dbb~Yhu3AtP3s1035810358epcas1p2T; Tue, 19 Nov 2019 09:40:25 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119094025epsmtrp1e0408cbcdc7c2c2aa15136d9b6f7c3f5~Yhu3ADYhZ0080100801epsmtrp1q; Tue, 19 Nov 2019 09:40:25 +0000 (GMT) X-AuditID: b6c32a39-913ff7000000108d-bd-5dd3b88a2f29 Received: from epsmtip2.samsung.com ( [182.195.34.31]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id 6E.92.03814.988B3DD5; Tue, 19 Nov 2019 18:40:25 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20191119094025epsmtip259c1b575cd4f21dfa5534a45e6bb6d9a~Yhu23d9Vx0817608176epsmtip2D; Tue, 19 Nov 2019 09:40:25 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, Namjae Jeon Subject: [PATCH v3 09/13] exfat: add misc operations Date: Tue, 19 Nov 2019 04:37:14 -0500 Message-Id: <20191119093718.3501-10-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119093718.3501-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA01Se0hTYRTn2727u4qLy9T8UnDrgpTvzTm9hutBEReSkKSCSOZFrw/cq90p qX+kFD5GmBVhzIyelg/SatnUXujMRySaPVCzUKPXojTNzMzadnv99zu/8zvnd77zHRyRHMIC 8Ry9mTXpGS2JeaOtXaFRkRb7cKr80gkvqmvqmIg6eL4Zo+obuwXUs/FRhLp1uw+lhttPYdRP 6xshtVB9gLItO4TUo0/T6EZvus06LqLv1jaJ6I6RYoyutDUA+vqDInr2WjDdefMDRo+9bkWT 8T3axGyWyWBNMlafbsjI0WepyW0pms0aVZxcEalIoOJJmZ7RsWpyS1Jy5NYcrWtEUpbPaPNc VDLDcWT0+kSTIc/MyrINnFlNssYMrVEhN0ZxjI7L02dFpRt06xRyeYzKpUzTZpdcsAPj44T9 N56XiopBf4QFeOGQiIUn+gcEFuCNSwg7gM7xXhEffAbwxZM5lA/mARys6sL+lJTXtArcWELc BrC22+dvxXTbYaEF4DhGhMMlm79b40dsgNdr7nkaIYQDwLdzVSJ3wtfVaMrZgLgxSoTAq9YK j4GYSIQllsMobyaFjS33PBovFz9RUwfcjSDRi8H5njHUbQaJLbC/Q8rrfeH7HpuIx4Hw3ZFS ES8pgjN3EZ4ud83wVc1jJRxpbvGMjBChsLk9mqdXw7bvtcCNEWIF/PiFfxUkxLC8VMJLQmDl oy4Bj4OgpWz6tykNH07OCvmNHAHw8uKysAoEW/85nAGgAaxkjZwui+UURtX//3UNeC4wLMEO 7g8kdQICB6SPWLZmOFUiZPK5Al0ngDhC+om3Tw6mSsQZTEEhazJoTHlalusEKtcejyKB/ukG 1z3rzRqFKkapVFKxcfFxKiUZIMYXhlIlRBZjZnNZ1sia/tQJcK/AYjA0EZGANxZappak6rTz +UvljuOnd4SnfRscndwb9HZfr3M2tGlAaltlPamrMZ5l5Np+JvqD74aW1dIJB179qmCxcirk pS6p7mksxp7dedR7FM9t0G+idldMrv1x0eHcBe1PUzJjWjXVzp7M/fV924rKzt2ZiJC9dNwK mJFdEZMkymUzijDExDG/AEg+UZWXAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrPLMWRmVeSWpSXmKPExsWy7bCSvG7njsuxBjObJC0OP57EbtG8eD2b xcrVR5ksrt+9xWyxZ+9JFovLu+awWfyf9ZzV4sf0eost/46wWlx6/4HFgctj56y77B77565h 99h9s4HNo2/LKkaPzaerPT5vkvM4tP0Nm8ftZ9tYAjiiuGxSUnMyy1KL9O0SuDIal+xgLLhi WbH1Tht7A+MpnS5GTg4JAROJjtnbmLoYuTiEBHYzShx9vpgRIiEtcezEGeYuRg4gW1ji8OFi iJoPjBJrHyxjAYmzCWhL/NkiClIuIuAo0bvrMAtIDbPAOUaJnc+Wgc0RBlrw+PUqZhCbRUBV YuOsTjYQm1fARqKxq4cFYpe8xOoNB8BqOIHiD2dD9AoJWEs0Pmpmn8DIt4CRYRWjZGpBcW56 brFhgVFearlecWJucWleul5yfu4mRnCwamntYDxxIv4QowAHoxIP7wmVy7FCrIllxZW5hxgl OJiVRHj9Hl2IFeJNSaysSi3Kjy8qzUktPsQozcGiJM4rn38sUkggPbEkNTs1tSC1CCbLxMEp 1cBosu/+srevw1Nv2ewuX1wZufrCxiUzpOK/h5k+vGoeLCZSrjz/OJ+Yr+3bSdnXi2Yunxoq VjlbKTU/R9M8ap/TXcNjISo+wRs3Lqwy77l//+H3S//Ut20z2xyrmL+uSE7SUCP4k/y7N5WP k3yLmdyfWHw3lk/S2CT064Q3h0TWk9sOK5dkC39WYinOSDTUYi4qTgQAZoaluVICAAA= X-CMS-MailID: 20191119094025epcas1p2bbb3121ac9f1d427f09f6548b8d35dbb X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119094025epcas1p2bbb3121ac9f1d427f09f6548b8d35dbb References: <20191119093718.3501-1-namjae.jeon@samsung.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This adds the implementation of misc operations for exfat. Signed-off-by: Namjae Jeon Signed-off-by: Sungjong Seo --- fs/exfat/misc.c | 247 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 fs/exfat/misc.c diff --git a/fs/exfat/misc.c b/fs/exfat/misc.c new file mode 100644 index 000000000000..19a140355cf0 --- /dev/null +++ b/fs/exfat/misc.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Written 1992,1993 by Werner Almesberger + * 22/11/2000 - Fixed fat_date_unix2dos for dates earlier than 01/01/1980 + * and date_dos2unix for date==0 by Igor Zhbanov(bsg@uniyar.ac.ru) + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include + +#include "exfat_raw.h" +#include "exfat_fs.h" + +/* + * exfat_fs_error reports a file system problem that might indicate fa data + * corruption/inconsistency. Depending on 'errors' mount option the + * panic() is called, or error message is printed FAT and nothing is done, + * or filesystem is remounted read-only (default behavior). + * In case the file system is remounted read-only, it can be made writable + * again by remounting it. + */ +void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...) +{ + struct exfat_mount_options *opts = &EXFAT_SB(sb)->options; + va_list args; + struct va_format vaf; + + if (report) { + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + exfat_msg(sb, KERN_ERR, "error, %pV\n", &vaf); + va_end(args); + } + + if (opts->errors == EXFAT_ERRORS_PANIC) { + panic("exFAT-fs (%s): fs panic from previous error\n", + sb->s_id); + } else if (opts->errors == EXFAT_ERRORS_RO && !sb_rdonly(sb)) { + sb->s_flags |= SB_RDONLY; + exfat_msg(sb, KERN_ERR, "Filesystem has been set read-only"); + } +} + +/* + * exfat_msg() - print preformated EXFAT specific messages. + * All logs except what uses exfat_fs_error() should be written by exfat_msg() + */ +void exfat_msg(struct super_block *sb, const char *level, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + va_start(args, fmt); + vaf.fmt = fmt; + vaf.va = &args; + /* level means KERN_ pacility level */ + printk("%sexFAT-fs (%s): %pV\n", level, sb->s_id, &vaf); + va_end(args); +} + +/* externs sys_tz + * extern struct timezone sys_tz; + */ +#define UNIX_SECS_1980 315532800L + +#if BITS_PER_LONG == 64 +#define UNIX_SECS_2108 4354819200L +#endif + +/* days between 1970/01/01 and 1980/01/01 (2 leap days) */ +#define DAYS_DELTA_DECADE (365 * 10 + 2) +/* 120 (2100 - 1980) isn't leap year */ +#define NO_LEAP_YEAR_2100 (120) +#define IS_LEAP_YEAR(y) (!((y) & 0x3) && (y) != NO_LEAP_YEAR_2100) + +#define SECS_PER_MIN (60) +#define SECS_PER_HOUR (60 * SECS_PER_MIN) +#define SECS_PER_DAY (24 * SECS_PER_HOUR) + +#define MAKE_LEAP_YEAR(leap_year, year) \ + do { \ + /* 2100 isn't leap year */ \ + if (unlikely(year > NO_LEAP_YEAR_2100)) \ + leap_year = ((year + 3) / 4) - 1; \ + else \ + leap_year = ((year + 3) / 4); \ + } while (0) + +/* Linear day numbers of the respective 1sts in non-leap years. */ +static time_t accum_days_in_year[] = { + /* Month : N 01 02 03 04 05 06 07 08 09 10 11 12 */ + 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, +}; + +/* Convert a FAT time/date pair to a UNIX date (seconds since 1 1 70). */ +void exfat_time_fat2unix(struct exfat_sb_info *sbi, struct timespec64 *ts, + struct exfat_date_time *tp) +{ + time_t year = tp->year; + time_t ld; /* leap day */ + + MAKE_LEAP_YEAR(ld, year); + + if (IS_LEAP_YEAR(year) && (tp->month) > 2) + ld++; + + ts->tv_sec = tp->second + tp->minute * SECS_PER_MIN + + tp->hour * SECS_PER_HOUR + + (year * 365 + ld + accum_days_in_year[tp->month] + + (tp->day - 1) + DAYS_DELTA_DECADE) * SECS_PER_DAY; + + if (!sbi->options.tz_utc) + ts->tv_sec += sys_tz.tz_minuteswest * SECS_PER_MIN; + + ts->tv_nsec = 0; +} + +/* Convert linear UNIX date to a FAT time/date pair. */ +void exfat_time_unix2fat(struct exfat_sb_info *sbi, struct timespec64 *ts, + struct exfat_date_time *tp) +{ + time_t second = ts->tv_sec; + time_t day, month, year; + time_t ld; /* leap day */ + + if (!sbi->options.tz_utc) + second -= sys_tz.tz_minuteswest * SECS_PER_MIN; + + /* Jan 1 GMT 00:00:00 1980. But what about another time zone? */ + if (second < UNIX_SECS_1980) { + tp->second = 0; + tp->minute = 0; + tp->hour = 0; + tp->day = 1; + tp->month = 1; + tp->year = 0; + return; + } +#if (BITS_PER_LONG == 64) + if (second >= UNIX_SECS_2108) { + tp->second = 59; + tp->minute = 59; + tp->hour = 23; + tp->day = 31; + tp->month = 12; + tp->year = 127; + return; + } +#endif + + day = second / SECS_PER_DAY - DAYS_DELTA_DECADE; + year = day / 365; + + MAKE_LEAP_YEAR(ld, year); + if (year * 365 + ld > day) + year--; + + MAKE_LEAP_YEAR(ld, year); + day -= year * 365 + ld; + + if (IS_LEAP_YEAR(year) && day == accum_days_in_year[3]) { + month = 2; + } else { + if (IS_LEAP_YEAR(year) && day > accum_days_in_year[3]) + day--; + for (month = 1; month < 12; month++) { + if (accum_days_in_year[month + 1] > day) + break; + } + } + day -= accum_days_in_year[month]; + + tp->second = second % SECS_PER_MIN; + tp->minute = (second / SECS_PER_MIN) % 60; + tp->hour = (second / SECS_PER_HOUR) % 24; + tp->day = day + 1; + tp->month = month; + tp->year = year; +} + +struct exfat_timestamp *exfat_tm_now(struct exfat_sb_info *sbi, + struct exfat_timestamp *tp) +{ + struct timespec64 ts; + struct exfat_date_time dt; + + ktime_get_real_ts64(&ts); + exfat_time_unix2fat(sbi, &ts, &dt); + + tp->year = dt.year; + tp->mon = dt.month; + tp->day = dt.day; + tp->hour = dt.hour; + tp->min = dt.minute; + tp->sec = dt.second; + + return tp; +} + +unsigned short exfat_calc_chksum_2byte(void *data, int len, + unsigned short chksum, int type) +{ + int i; + unsigned char *c = (unsigned char *)data; + + for (i = 0; i < len; i++, c++) { + if (((i == 2) || (i == 3)) && (type == CS_DIR_ENTRY)) + continue; + chksum = (((chksum & 1) << 15) | ((chksum & 0xFFFE) >> 1)) + + (unsigned short)*c; + } + return chksum; +} + +void exfat_update_bh(struct super_block *sb, struct buffer_head *bh, int sync) +{ + WRITE_ONCE(EXFAT_SB(sb)->s_dirt, true); + set_buffer_uptodate(bh); + mark_buffer_dirty(bh); + + if (sync) + sync_dirty_buffer(bh); +} + +void exfat_chain_set(struct exfat_chain *ec, unsigned int dir, + unsigned int size, unsigned char flags) +{ + ec->dir = dir; + ec->size = size; + ec->flags = flags; +} + +struct exfat_chain *exfat_chain_dup(struct exfat_chain *ec) +{ + struct exfat_chain *dup; + + dup = kmalloc(sizeof(struct exfat_chain), GFP_KERNEL); + if (!dup) + return NULL; + + exfat_chain_set(dup, ec->dir, ec->size, ec->flags); + return dup; +} From patchwork Tue Nov 19 09:37:15 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11251623 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9B6A66C1 for ; Tue, 19 Nov 2019 09:40:43 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 52CC722312 for ; Tue, 19 Nov 2019 09:40:43 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="rPp/33bx" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727706AbfKSJkm (ORCPT ); Tue, 19 Nov 2019 04:40:42 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:14090 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727587AbfKSJkg (ORCPT ); Tue, 19 Nov 2019 04:40:36 -0500 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20191119094029epoutp04e5b2011dd2a7bfb4c87a8317bd364239~Yhu6VFRoQ0888908889epoutp04V for ; Tue, 19 Nov 2019 09:40:29 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20191119094029epoutp04e5b2011dd2a7bfb4c87a8317bd364239~Yhu6VFRoQ0888908889epoutp04V DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574156429; bh=sg6rBMV6EJtwTkWZmrtWVl0lnSXUM5YyRcAV+lePukM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rPp/33bxpv6c9pJALR33g+AH/2VzWE514gpNu3Zqs/JI/SfMujNsQzPNwLQvN6nIj rDftL5a1BezdXYu8eaX9W3WAucQR2z9hVBJIJ2HM4KTgid8yBRy5y+U2zVCBoL6XGS CLm1Mc9IDAuer7EGqyeX4lRjd7c0CianFkeqAKM8= Received: from epsnrtp1.localdomain (unknown [182.195.42.162]) by epcas1p4.samsung.com (KnoxPortal) with ESMTP id 20191119094028epcas1p424145fc4c14166c70ee3c3024b7fb396~Yhu6EFk1p0069700697epcas1p4l; Tue, 19 Nov 2019 09:40:28 +0000 (GMT) Received: from epsmges1p4.samsung.com (unknown [182.195.40.159]) by epsnrtp1.localdomain (Postfix) with ESMTP id 47HLQz4PHYzMqYlh; Tue, 19 Nov 2019 09:40:27 +0000 (GMT) Received: from epcas1p2.samsung.com ( [182.195.41.46]) by epsmges1p4.samsung.com (Symantec Messaging Gateway) with SMTP id 33.A4.04406.A88B3DD5; Tue, 19 Nov 2019 18:40:26 +0900 (KST) Received: from epsmtrp2.samsung.com (unknown [182.195.40.14]) by epcas1p3.samsung.com (KnoxPortal) with ESMTPA id 20191119094026epcas1p3eea5c655f3b89383e02c0097c491f0bc~Yhu3sv2B00934409344epcas1p3c; Tue, 19 Nov 2019 09:40:26 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp2.samsung.com (KnoxPortal) with ESMTP id 20191119094026epsmtrp23423c19ccfa45a0666b10a531c7076b5~Yhu3roNXA0322303223epsmtrp2i; Tue, 19 Nov 2019 09:40:26 +0000 (GMT) X-AuditID: b6c32a38-947ff70000001136-3d-5dd3b88a1d78 Received: from epsmtip2.samsung.com ( [182.195.34.31]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id 1F.92.03814.A88B3DD5; Tue, 19 Nov 2019 18:40:26 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20191119094025epsmtip2e64f6e53eb6a612d044b41e64ae21ead~Yhu3fG7PY0597405974epsmtip2e; Tue, 19 Nov 2019 09:40:25 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, Namjae Jeon Subject: [PATCH v3 10/13] exfat: add nls operations Date: Tue, 19 Nov 2019 04:37:15 -0500 Message-Id: <20191119093718.3501-11-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119093718.3501-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA01SWUwTURTN60ynA1qd1O2lRpYJJoIpdizFUUExEpxENBj/TEiZ0Akg3dJp iegHRCNg3VCixmJVcAWMUGjYES0YcIlRUVlcwQVEoiiiIIi2HVz+zj33nHvvu+/iiCwXk+Np BgtnNrA6EvNHq1tCwxW22o5E5WRBGN3y+qiE3nOuHKNLym6K6M7nPQjd2HQLpTvqT2H0L3u/ mB47kUW7plrF9MNPw2iMP1Nnfy5hmh1XJExDdzbGHHKVAqbqzi5mpDKAcdcMYczTd9VoAr5N F5XKsVrOHMQZko3aNENKNLlxq2a9Rh2ppBTUSnoFGWRg9Vw0GRufoIhL03lGJIMyWJ3VQyWw PE8uWxNlNlotXFCqkbdEk5xJqzNRSlM4z+p5qyElPNmoX0UplcvVHmWSLvWsswY1/Tgp2tFU tAfLBu/fAxvwwyERAW8V3BDZgD8uI2oB3O8cQITgC4DFRTnTwTcAP5QN/7UUtfWLhUQTgPcc v1Bvwmd5cTHOBnAcI5bCSdc8Lz2XWAurCq+jXj1CtAI48DVf4k3MIVSw8WWuz4sSi2Hx2y7E i6VEFBwcHUCEZoGwrOK6D/t5+N7Ci8BbCBJ3Mdj+qgQTRLFw3L132jAHDra5JAKWw5GPTZh3 IEjsgp+bpyV5niG+RwtYBbvLK8ReCUKEwvL6ZQIdDOsmHL73IsQs+HH0gFioIoV5OTJBshge etgiEvBCaMsdnm7KwFZHl0RYz2EAzx87AfJBgP1fh7MAlIL5nInXp3A8ZYr4/8sqge8Iw+ha 0Hgv3g0IHJAzpe0hHYkyMZvBZ+rdAOIIOVe6ue9+okyqZTN3cmajxmzVcbwbqD2LPILI5yUb PSdtsGgo9XKVSkVHRK6IVKvIBVJ87EGijEhhLVw6x5k48x+fCPeTZwNKSZmQ3gnttb6hkbdx Ba4n1wJ0Ic/S/R4FLxi6MLRkNNbee253oXPWpkfj1vSDebgqeyp/c4y94evUdnkIrundstqR 22APtheeySjZoIgPXN/jfCNp7NSEX+qfnbUoaV/76fnriq1l9GVn86f6mrbj6tsK/7GrMzhV xWNj6PDPkyTKp7JUGGLm2d8wdVFZmgMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrALMWRmVeSWpSXmKPExsWy7bCSvG7XjsuxBtO62SwOP57EbtG8eD2b xcrVR5ksrt+9xWyxZ+9JFovLu+awWfyf9ZzV4sf0eost/46wWlx6/4HFgctj56y77B77565h 99h9s4HNo2/LKkaPzaerPT5vkvM4tP0Nm8ftZ9tYAjiiuGxSUnMyy1KL9O0SuDIWbNzOUvBr JlPF3oXNbA2ML18ydjFyckgImEgsPP6cFcQWEtjNKHFhgjZEXFri2IkzzF2MHEC2sMThw8Vd jFxAJR8YJVY938QCEmcT0Jb4s0UUpFxEwFGid9dhFpAaZoFzjBI7ny0Dmy8sYCyx5347C4jN IqAqsejpDWYQm1fARuLV1xfMELvkJVZvOABmcwLFH86G6BUSsJZofNTMPoGRbwEjwypGydSC 4tz03GLDAqO81HK94sTc4tK8dL3k/NxNjOBw1dLawXjiRPwhRgEORiUe3hMql2OFWBPLiitz DzFKcDArifD6PboQK8SbklhZlVqUH19UmpNafIhRmoNFSZxXPv9YpJBAemJJanZqakFqEUyW iYNTqoFxWYLSzNbw/qzTt3rur93Oy6CZq65q16z8rmWlhCKj6FrDnyEa0u+v+tcKHec96im/ Mazd5GHAlpc75ddVnla64OB8KtXslma/mTvjRP7/nOqnd926HP+tqG3+A25r/9e10n+TVrJJ WL6ctc9if/qTMx836L1limg+s6DibaZf6+PJ6X63nk1RYinOSDTUYi4qTgQA+r5GnFMCAAA= X-CMS-MailID: 20191119094026epcas1p3eea5c655f3b89383e02c0097c491f0bc X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119094026epcas1p3eea5c655f3b89383e02c0097c491f0bc References: <20191119093718.3501-1-namjae.jeon@samsung.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This adds the implementation of nls operations for exfat. Signed-off-by: Namjae Jeon Signed-off-by: Sungjong Seo --- fs/exfat/nls.c | 817 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 817 insertions(+) create mode 100644 fs/exfat/nls.c diff --git a/fs/exfat/nls.c b/fs/exfat/nls.c new file mode 100644 index 000000000000..446441e67575 --- /dev/null +++ b/fs/exfat/nls.c @@ -0,0 +1,817 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2012-2013 Samsung Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include + +#include "exfat_raw.h" +#include "exfat_fs.h" + +/* Upcase tabel macro */ +#define EXFAT_NUM_UPCASE (2918) +#define UTBL_COUNT (0x10000) + +/* + * Upcase table in compressed format (7.2.5.1 Recommended Up-case Table + * in exfat specification, See: https://docs.microsoft.com/en-us/windows/ + * win32/fileio/exfat-specification). + */ +static const unsigned short uni_def_upcase[EXFAT_NUM_UPCASE] = { + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, + 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, + 0x0060, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, + 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, + 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, + 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, + 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, + 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, + 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, + 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, + 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, + 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, + 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, + 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, + 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, + 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, + 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, + 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00f7, + 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x0178, + 0x0100, 0x0100, 0x0102, 0x0102, 0x0104, 0x0104, 0x0106, 0x0106, + 0x0108, 0x0108, 0x010a, 0x010a, 0x010c, 0x010c, 0x010e, 0x010e, + 0x0110, 0x0110, 0x0112, 0x0112, 0x0114, 0x0114, 0x0116, 0x0116, + 0x0118, 0x0118, 0x011a, 0x011a, 0x011c, 0x011c, 0x011e, 0x011e, + 0x0120, 0x0120, 0x0122, 0x0122, 0x0124, 0x0124, 0x0126, 0x0126, + 0x0128, 0x0128, 0x012a, 0x012a, 0x012c, 0x012c, 0x012e, 0x012e, + 0x0130, 0x0131, 0x0132, 0x0132, 0x0134, 0x0134, 0x0136, 0x0136, + 0x0138, 0x0139, 0x0139, 0x013b, 0x013b, 0x013d, 0x013d, 0x013f, + 0x013f, 0x0141, 0x0141, 0x0143, 0x0143, 0x0145, 0x0145, 0x0147, + 0x0147, 0x0149, 0x014a, 0x014a, 0x014c, 0x014c, 0x014e, 0x014e, + 0x0150, 0x0150, 0x0152, 0x0152, 0x0154, 0x0154, 0x0156, 0x0156, + 0x0158, 0x0158, 0x015a, 0x015a, 0x015c, 0x015c, 0x015e, 0x015e, + 0x0160, 0x0160, 0x0162, 0x0162, 0x0164, 0x0164, 0x0166, 0x0166, + 0x0168, 0x0168, 0x016a, 0x016a, 0x016c, 0x016c, 0x016e, 0x016e, + 0x0170, 0x0170, 0x0172, 0x0172, 0x0174, 0x0174, 0x0176, 0x0176, + 0x0178, 0x0179, 0x0179, 0x017b, 0x017b, 0x017d, 0x017d, 0x017f, + 0x0243, 0x0181, 0x0182, 0x0182, 0x0184, 0x0184, 0x0186, 0x0187, + 0x0187, 0x0189, 0x018a, 0x018b, 0x018b, 0x018d, 0x018e, 0x018f, + 0x0190, 0x0191, 0x0191, 0x0193, 0x0194, 0x01f6, 0x0196, 0x0197, + 0x0198, 0x0198, 0x023d, 0x019b, 0x019c, 0x019d, 0x0220, 0x019f, + 0x01a0, 0x01a0, 0x01a2, 0x01a2, 0x01a4, 0x01a4, 0x01a6, 0x01a7, + 0x01a7, 0x01a9, 0x01aa, 0x01ab, 0x01ac, 0x01ac, 0x01ae, 0x01af, + 0x01af, 0x01b1, 0x01b2, 0x01b3, 0x01b3, 0x01b5, 0x01b5, 0x01b7, + 0x01b8, 0x01b8, 0x01ba, 0x01bb, 0x01bc, 0x01bc, 0x01be, 0x01f7, + 0x01c0, 0x01c1, 0x01c2, 0x01c3, 0x01c4, 0x01c5, 0x01c4, 0x01c7, + 0x01c8, 0x01c7, 0x01ca, 0x01cb, 0x01ca, 0x01cd, 0x01cd, 0x01cf, + 0x01cf, 0x01d1, 0x01d1, 0x01d3, 0x01d3, 0x01d5, 0x01d5, 0x01d7, + 0x01d7, 0x01d9, 0x01d9, 0x01db, 0x01db, 0x018e, 0x01de, 0x01de, + 0x01e0, 0x01e0, 0x01e2, 0x01e2, 0x01e4, 0x01e4, 0x01e6, 0x01e6, + 0x01e8, 0x01e8, 0x01ea, 0x01ea, 0x01ec, 0x01ec, 0x01ee, 0x01ee, + 0x01f0, 0x01f1, 0x01f2, 0x01f1, 0x01f4, 0x01f4, 0x01f6, 0x01f7, + 0x01f8, 0x01f8, 0x01fa, 0x01fa, 0x01fc, 0x01fc, 0x01fe, 0x01fe, + 0x0200, 0x0200, 0x0202, 0x0202, 0x0204, 0x0204, 0x0206, 0x0206, + 0x0208, 0x0208, 0x020a, 0x020a, 0x020c, 0x020c, 0x020e, 0x020e, + 0x0210, 0x0210, 0x0212, 0x0212, 0x0214, 0x0214, 0x0216, 0x0216, + 0x0218, 0x0218, 0x021a, 0x021a, 0x021c, 0x021c, 0x021e, 0x021e, + 0x0220, 0x0221, 0x0222, 0x0222, 0x0224, 0x0224, 0x0226, 0x0226, + 0x0228, 0x0228, 0x022a, 0x022a, 0x022c, 0x022c, 0x022e, 0x022e, + 0x0230, 0x0230, 0x0232, 0x0232, 0x0234, 0x0235, 0x0236, 0x0237, + 0x0238, 0x0239, 0x2c65, 0x023b, 0x023b, 0x023d, 0x2c66, 0x023f, + 0x0240, 0x0241, 0x0241, 0x0243, 0x0244, 0x0245, 0x0246, 0x0246, + 0x0248, 0x0248, 0x024a, 0x024a, 0x024c, 0x024c, 0x024e, 0x024e, + 0x0250, 0x0251, 0x0252, 0x0181, 0x0186, 0x0255, 0x0189, 0x018a, + 0x0258, 0x018f, 0x025a, 0x0190, 0x025c, 0x025d, 0x025e, 0x025f, + 0x0193, 0x0261, 0x0262, 0x0194, 0x0264, 0x0265, 0x0266, 0x0267, + 0x0197, 0x0196, 0x026a, 0x2c62, 0x026c, 0x026d, 0x026e, 0x019c, + 0x0270, 0x0271, 0x019d, 0x0273, 0x0274, 0x019f, 0x0276, 0x0277, + 0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x2c64, 0x027e, 0x027f, + 0x01a6, 0x0281, 0x0282, 0x01a9, 0x0284, 0x0285, 0x0286, 0x0287, + 0x01ae, 0x0244, 0x01b1, 0x01b2, 0x0245, 0x028d, 0x028e, 0x028f, + 0x0290, 0x0291, 0x01b7, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, + 0x0298, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029e, 0x029f, + 0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a4, 0x02a5, 0x02a6, 0x02a7, + 0x02a8, 0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02af, + 0x02b0, 0x02b1, 0x02b2, 0x02b3, 0x02b4, 0x02b5, 0x02b6, 0x02b7, + 0x02b8, 0x02b9, 0x02ba, 0x02bb, 0x02bc, 0x02bd, 0x02be, 0x02bf, + 0x02c0, 0x02c1, 0x02c2, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c7, + 0x02c8, 0x02c9, 0x02ca, 0x02cb, 0x02cc, 0x02cd, 0x02ce, 0x02cf, + 0x02d0, 0x02d1, 0x02d2, 0x02d3, 0x02d4, 0x02d5, 0x02d6, 0x02d7, + 0x02d8, 0x02d9, 0x02da, 0x02db, 0x02dc, 0x02dd, 0x02de, 0x02df, + 0x02e0, 0x02e1, 0x02e2, 0x02e3, 0x02e4, 0x02e5, 0x02e6, 0x02e7, + 0x02e8, 0x02e9, 0x02ea, 0x02eb, 0x02ec, 0x02ed, 0x02ee, 0x02ef, + 0x02f0, 0x02f1, 0x02f2, 0x02f3, 0x02f4, 0x02f5, 0x02f6, 0x02f7, + 0x02f8, 0x02f9, 0x02fa, 0x02fb, 0x02fc, 0x02fd, 0x02fe, 0x02ff, + 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, + 0x0308, 0x0309, 0x030a, 0x030b, 0x030c, 0x030d, 0x030e, 0x030f, + 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, + 0x0318, 0x0319, 0x031a, 0x031b, 0x031c, 0x031d, 0x031e, 0x031f, + 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, + 0x0328, 0x0329, 0x032a, 0x032b, 0x032c, 0x032d, 0x032e, 0x032f, + 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, + 0x0338, 0x0339, 0x033a, 0x033b, 0x033c, 0x033d, 0x033e, 0x033f, + 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347, + 0x0348, 0x0349, 0x034a, 0x034b, 0x034c, 0x034d, 0x034e, 0x034f, + 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, + 0x0358, 0x0359, 0x035a, 0x035b, 0x035c, 0x035d, 0x035e, 0x035f, + 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, + 0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f, + 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377, + 0x0378, 0x0379, 0x037a, 0x03fd, 0x03fe, 0x03ff, 0x037e, 0x037f, + 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387, + 0x0388, 0x0389, 0x038a, 0x038b, 0x038c, 0x038d, 0x038e, 0x038f, + 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, + 0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, + 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x0386, 0x0388, 0x0389, 0x038a, + 0x03b0, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, + 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, + 0x03a0, 0x03a1, 0x03a3, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, + 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x038c, 0x038e, 0x038f, 0x03cf, + 0x03d0, 0x03d1, 0x03d2, 0x03d3, 0x03d4, 0x03d5, 0x03d6, 0x03d7, + 0x03d8, 0x03d8, 0x03da, 0x03da, 0x03dc, 0x03dc, 0x03de, 0x03de, + 0x03e0, 0x03e0, 0x03e2, 0x03e2, 0x03e4, 0x03e4, 0x03e6, 0x03e6, + 0x03e8, 0x03e8, 0x03ea, 0x03ea, 0x03ec, 0x03ec, 0x03ee, 0x03ee, + 0x03f0, 0x03f1, 0x03f9, 0x03f3, 0x03f4, 0x03f5, 0x03f6, 0x03f7, + 0x03f7, 0x03f9, 0x03fa, 0x03fa, 0x03fc, 0x03fd, 0x03fe, 0x03ff, + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, + 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, + 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, + 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, + 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, + 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, + 0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, + 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x040d, 0x040e, 0x040f, + 0x0460, 0x0460, 0x0462, 0x0462, 0x0464, 0x0464, 0x0466, 0x0466, + 0x0468, 0x0468, 0x046a, 0x046a, 0x046c, 0x046c, 0x046e, 0x046e, + 0x0470, 0x0470, 0x0472, 0x0472, 0x0474, 0x0474, 0x0476, 0x0476, + 0x0478, 0x0478, 0x047a, 0x047a, 0x047c, 0x047c, 0x047e, 0x047e, + 0x0480, 0x0480, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, + 0x0488, 0x0489, 0x048a, 0x048a, 0x048c, 0x048c, 0x048e, 0x048e, + 0x0490, 0x0490, 0x0492, 0x0492, 0x0494, 0x0494, 0x0496, 0x0496, + 0x0498, 0x0498, 0x049a, 0x049a, 0x049c, 0x049c, 0x049e, 0x049e, + 0x04a0, 0x04a0, 0x04a2, 0x04a2, 0x04a4, 0x04a4, 0x04a6, 0x04a6, + 0x04a8, 0x04a8, 0x04aa, 0x04aa, 0x04ac, 0x04ac, 0x04ae, 0x04ae, + 0x04b0, 0x04b0, 0x04b2, 0x04b2, 0x04b4, 0x04b4, 0x04b6, 0x04b6, + 0x04b8, 0x04b8, 0x04ba, 0x04ba, 0x04bc, 0x04bc, 0x04be, 0x04be, + 0x04c0, 0x04c1, 0x04c1, 0x04c3, 0x04c3, 0x04c5, 0x04c5, 0x04c7, + 0x04c7, 0x04c9, 0x04c9, 0x04cb, 0x04cb, 0x04cd, 0x04cd, 0x04c0, + 0x04d0, 0x04d0, 0x04d2, 0x04d2, 0x04d4, 0x04d4, 0x04d6, 0x04d6, + 0x04d8, 0x04d8, 0x04da, 0x04da, 0x04dc, 0x04dc, 0x04de, 0x04de, + 0x04e0, 0x04e0, 0x04e2, 0x04e2, 0x04e4, 0x04e4, 0x04e6, 0x04e6, + 0x04e8, 0x04e8, 0x04ea, 0x04ea, 0x04ec, 0x04ec, 0x04ee, 0x04ee, + 0x04f0, 0x04f0, 0x04f2, 0x04f2, 0x04f4, 0x04f4, 0x04f6, 0x04f6, + 0x04f8, 0x04f8, 0x04fa, 0x04fa, 0x04fc, 0x04fc, 0x04fe, 0x04fe, + 0x0500, 0x0500, 0x0502, 0x0502, 0x0504, 0x0504, 0x0506, 0x0506, + 0x0508, 0x0508, 0x050a, 0x050a, 0x050c, 0x050c, 0x050e, 0x050e, + 0x0510, 0x0510, 0x0512, 0x0512, 0x0514, 0x0515, 0x0516, 0x0517, + 0x0518, 0x0519, 0x051a, 0x051b, 0x051c, 0x051d, 0x051e, 0x051f, + 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527, + 0x0528, 0x0529, 0x052a, 0x052b, 0x052c, 0x052d, 0x052e, 0x052f, + 0x0530, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537, + 0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f, + 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547, + 0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f, + 0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0x0557, + 0x0558, 0x0559, 0x055a, 0x055b, 0x055c, 0x055d, 0x055e, 0x055f, + 0x0560, 0x0531, 0x0532, 0x0533, 0x0534, 0x0535, 0x0536, 0x0537, + 0x0538, 0x0539, 0x053a, 0x053b, 0x053c, 0x053d, 0x053e, 0x053f, + 0x0540, 0x0541, 0x0542, 0x0543, 0x0544, 0x0545, 0x0546, 0x0547, + 0x0548, 0x0549, 0x054a, 0x054b, 0x054c, 0x054d, 0x054e, 0x054f, + 0x0550, 0x0551, 0x0552, 0x0553, 0x0554, 0x0555, 0x0556, 0xffff, + 0x17f6, 0x2c63, 0x1d7e, 0x1d7f, 0x1d80, 0x1d81, 0x1d82, 0x1d83, + 0x1d84, 0x1d85, 0x1d86, 0x1d87, 0x1d88, 0x1d89, 0x1d8a, 0x1d8b, + 0x1d8c, 0x1d8d, 0x1d8e, 0x1d8f, 0x1d90, 0x1d91, 0x1d92, 0x1d93, + 0x1d94, 0x1d95, 0x1d96, 0x1d97, 0x1d98, 0x1d99, 0x1d9a, 0x1d9b, + 0x1d9c, 0x1d9d, 0x1d9e, 0x1d9f, 0x1da0, 0x1da1, 0x1da2, 0x1da3, + 0x1da4, 0x1da5, 0x1da6, 0x1da7, 0x1da8, 0x1da9, 0x1daa, 0x1dab, + 0x1dac, 0x1dad, 0x1dae, 0x1daf, 0x1db0, 0x1db1, 0x1db2, 0x1db3, + 0x1db4, 0x1db5, 0x1db6, 0x1db7, 0x1db8, 0x1db9, 0x1dba, 0x1dbb, + 0x1dbc, 0x1dbd, 0x1dbe, 0x1dbf, 0x1dc0, 0x1dc1, 0x1dc2, 0x1dc3, + 0x1dc4, 0x1dc5, 0x1dc6, 0x1dc7, 0x1dc8, 0x1dc9, 0x1dca, 0x1dcb, + 0x1dcc, 0x1dcd, 0x1dce, 0x1dcf, 0x1dd0, 0x1dd1, 0x1dd2, 0x1dd3, + 0x1dd4, 0x1dd5, 0x1dd6, 0x1dd7, 0x1dd8, 0x1dd9, 0x1dda, 0x1ddb, + 0x1ddc, 0x1ddd, 0x1dde, 0x1ddf, 0x1de0, 0x1de1, 0x1de2, 0x1de3, + 0x1de4, 0x1de5, 0x1de6, 0x1de7, 0x1de8, 0x1de9, 0x1dea, 0x1deb, + 0x1dec, 0x1ded, 0x1dee, 0x1def, 0x1df0, 0x1df1, 0x1df2, 0x1df3, + 0x1df4, 0x1df5, 0x1df6, 0x1df7, 0x1df8, 0x1df9, 0x1dfa, 0x1dfb, + 0x1dfc, 0x1dfd, 0x1dfe, 0x1dff, 0x1e00, 0x1e00, 0x1e02, 0x1e02, + 0x1e04, 0x1e04, 0x1e06, 0x1e06, 0x1e08, 0x1e08, 0x1e0a, 0x1e0a, + 0x1e0c, 0x1e0c, 0x1e0e, 0x1e0e, 0x1e10, 0x1e10, 0x1e12, 0x1e12, + 0x1e14, 0x1e14, 0x1e16, 0x1e16, 0x1e18, 0x1e18, 0x1e1a, 0x1e1a, + 0x1e1c, 0x1e1c, 0x1e1e, 0x1e1e, 0x1e20, 0x1e20, 0x1e22, 0x1e22, + 0x1e24, 0x1e24, 0x1e26, 0x1e26, 0x1e28, 0x1e28, 0x1e2a, 0x1e2a, + 0x1e2c, 0x1e2c, 0x1e2e, 0x1e2e, 0x1e30, 0x1e30, 0x1e32, 0x1e32, + 0x1e34, 0x1e34, 0x1e36, 0x1e36, 0x1e38, 0x1e38, 0x1e3a, 0x1e3a, + 0x1e3c, 0x1e3c, 0x1e3e, 0x1e3e, 0x1e40, 0x1e40, 0x1e42, 0x1e42, + 0x1e44, 0x1e44, 0x1e46, 0x1e46, 0x1e48, 0x1e48, 0x1e4a, 0x1e4a, + 0x1e4c, 0x1e4c, 0x1e4e, 0x1e4e, 0x1e50, 0x1e50, 0x1e52, 0x1e52, + 0x1e54, 0x1e54, 0x1e56, 0x1e56, 0x1e58, 0x1e58, 0x1e5a, 0x1e5a, + 0x1e5c, 0x1e5c, 0x1e5e, 0x1e5e, 0x1e60, 0x1e60, 0x1e62, 0x1e62, + 0x1e64, 0x1e64, 0x1e66, 0x1e66, 0x1e68, 0x1e68, 0x1e6a, 0x1e6a, + 0x1e6c, 0x1e6c, 0x1e6e, 0x1e6e, 0x1e70, 0x1e70, 0x1e72, 0x1e72, + 0x1e74, 0x1e74, 0x1e76, 0x1e76, 0x1e78, 0x1e78, 0x1e7a, 0x1e7a, + 0x1e7c, 0x1e7c, 0x1e7e, 0x1e7e, 0x1e80, 0x1e80, 0x1e82, 0x1e82, + 0x1e84, 0x1e84, 0x1e86, 0x1e86, 0x1e88, 0x1e88, 0x1e8a, 0x1e8a, + 0x1e8c, 0x1e8c, 0x1e8e, 0x1e8e, 0x1e90, 0x1e90, 0x1e92, 0x1e92, + 0x1e94, 0x1e94, 0x1e96, 0x1e97, 0x1e98, 0x1e99, 0x1e9a, 0x1e9b, + 0x1e9c, 0x1e9d, 0x1e9e, 0x1e9f, 0x1ea0, 0x1ea0, 0x1ea2, 0x1ea2, + 0x1ea4, 0x1ea4, 0x1ea6, 0x1ea6, 0x1ea8, 0x1ea8, 0x1eaa, 0x1eaa, + 0x1eac, 0x1eac, 0x1eae, 0x1eae, 0x1eb0, 0x1eb0, 0x1eb2, 0x1eb2, + 0x1eb4, 0x1eb4, 0x1eb6, 0x1eb6, 0x1eb8, 0x1eb8, 0x1eba, 0x1eba, + 0x1ebc, 0x1ebc, 0x1ebe, 0x1ebe, 0x1ec0, 0x1ec0, 0x1ec2, 0x1ec2, + 0x1ec4, 0x1ec4, 0x1ec6, 0x1ec6, 0x1ec8, 0x1ec8, 0x1eca, 0x1eca, + 0x1ecc, 0x1ecc, 0x1ece, 0x1ece, 0x1ed0, 0x1ed0, 0x1ed2, 0x1ed2, + 0x1ed4, 0x1ed4, 0x1ed6, 0x1ed6, 0x1ed8, 0x1ed8, 0x1eda, 0x1eda, + 0x1edc, 0x1edc, 0x1ede, 0x1ede, 0x1ee0, 0x1ee0, 0x1ee2, 0x1ee2, + 0x1ee4, 0x1ee4, 0x1ee6, 0x1ee6, 0x1ee8, 0x1ee8, 0x1eea, 0x1eea, + 0x1eec, 0x1eec, 0x1eee, 0x1eee, 0x1ef0, 0x1ef0, 0x1ef2, 0x1ef2, + 0x1ef4, 0x1ef4, 0x1ef6, 0x1ef6, 0x1ef8, 0x1ef8, 0x1efa, 0x1efb, + 0x1efc, 0x1efd, 0x1efe, 0x1eff, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, + 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f08, 0x1f09, 0x1f0a, 0x1f0b, + 0x1f0c, 0x1f0d, 0x1f0e, 0x1f0f, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, + 0x1f1c, 0x1f1d, 0x1f16, 0x1f17, 0x1f18, 0x1f19, 0x1f1a, 0x1f1b, + 0x1f1c, 0x1f1d, 0x1f1e, 0x1f1f, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, + 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f28, 0x1f29, 0x1f2a, 0x1f2b, + 0x1f2c, 0x1f2d, 0x1f2e, 0x1f2f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, + 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f38, 0x1f39, 0x1f3a, 0x1f3b, + 0x1f3c, 0x1f3d, 0x1f3e, 0x1f3f, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, + 0x1f4c, 0x1f4d, 0x1f46, 0x1f47, 0x1f48, 0x1f49, 0x1f4a, 0x1f4b, + 0x1f4c, 0x1f4d, 0x1f4e, 0x1f4f, 0x1f50, 0x1f59, 0x1f52, 0x1f5b, + 0x1f54, 0x1f5d, 0x1f56, 0x1f5f, 0x1f58, 0x1f59, 0x1f5a, 0x1f5b, + 0x1f5c, 0x1f5d, 0x1f5e, 0x1f5f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, + 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1f68, 0x1f69, 0x1f6a, 0x1f6b, + 0x1f6c, 0x1f6d, 0x1f6e, 0x1f6f, 0x1fba, 0x1fbb, 0x1fc8, 0x1fc9, + 0x1fca, 0x1fcb, 0x1fda, 0x1fdb, 0x1ff8, 0x1ff9, 0x1fea, 0x1feb, + 0x1ffa, 0x1ffb, 0x1f7e, 0x1f7f, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, + 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f88, 0x1f89, 0x1f8a, 0x1f8b, + 0x1f8c, 0x1f8d, 0x1f8e, 0x1f8f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, + 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1f98, 0x1f99, 0x1f9a, 0x1f9b, + 0x1f9c, 0x1f9d, 0x1f9e, 0x1f9f, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, + 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fa8, 0x1fa9, 0x1faa, 0x1fab, + 0x1fac, 0x1fad, 0x1fae, 0x1faf, 0x1fb8, 0x1fb9, 0x1fb2, 0x1fbc, + 0x1fb4, 0x1fb5, 0x1fb6, 0x1fb7, 0x1fb8, 0x1fb9, 0x1fba, 0x1fbb, + 0x1fbc, 0x1fbd, 0x1fbe, 0x1fbf, 0x1fc0, 0x1fc1, 0x1fc2, 0x1fc3, + 0x1fc4, 0x1fc5, 0x1fc6, 0x1fc7, 0x1fc8, 0x1fc9, 0x1fca, 0x1fcb, + 0x1fc3, 0x1fcd, 0x1fce, 0x1fcf, 0x1fd8, 0x1fd9, 0x1fd2, 0x1fd3, + 0x1fd4, 0x1fd5, 0x1fd6, 0x1fd7, 0x1fd8, 0x1fd9, 0x1fda, 0x1fdb, + 0x1fdc, 0x1fdd, 0x1fde, 0x1fdf, 0x1fe8, 0x1fe9, 0x1fe2, 0x1fe3, + 0x1fe4, 0x1fec, 0x1fe6, 0x1fe7, 0x1fe8, 0x1fe9, 0x1fea, 0x1feb, + 0x1fec, 0x1fed, 0x1fee, 0x1fef, 0x1ff0, 0x1ff1, 0x1ff2, 0x1ff3, + 0x1ff4, 0x1ff5, 0x1ff6, 0x1ff7, 0x1ff8, 0x1ff9, 0x1ffa, 0x1ffb, + 0x1ff3, 0x1ffd, 0x1ffe, 0x1fff, 0x2000, 0x2001, 0x2002, 0x2003, + 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200a, 0x200b, + 0x200c, 0x200d, 0x200e, 0x200f, 0x2010, 0x2011, 0x2012, 0x2013, + 0x2014, 0x2015, 0x2016, 0x2017, 0x2018, 0x2019, 0x201a, 0x201b, + 0x201c, 0x201d, 0x201e, 0x201f, 0x2020, 0x2021, 0x2022, 0x2023, + 0x2024, 0x2025, 0x2026, 0x2027, 0x2028, 0x2029, 0x202a, 0x202b, + 0x202c, 0x202d, 0x202e, 0x202f, 0x2030, 0x2031, 0x2032, 0x2033, + 0x2034, 0x2035, 0x2036, 0x2037, 0x2038, 0x2039, 0x203a, 0x203b, + 0x203c, 0x203d, 0x203e, 0x203f, 0x2040, 0x2041, 0x2042, 0x2043, + 0x2044, 0x2045, 0x2046, 0x2047, 0x2048, 0x2049, 0x204a, 0x204b, + 0x204c, 0x204d, 0x204e, 0x204f, 0x2050, 0x2051, 0x2052, 0x2053, + 0x2054, 0x2055, 0x2056, 0x2057, 0x2058, 0x2059, 0x205a, 0x205b, + 0x205c, 0x205d, 0x205e, 0x205f, 0x2060, 0x2061, 0x2062, 0x2063, + 0x2064, 0x2065, 0x2066, 0x2067, 0x2068, 0x2069, 0x206a, 0x206b, + 0x206c, 0x206d, 0x206e, 0x206f, 0x2070, 0x2071, 0x2072, 0x2073, + 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079, 0x207a, 0x207b, + 0x207c, 0x207d, 0x207e, 0x207f, 0x2080, 0x2081, 0x2082, 0x2083, + 0x2084, 0x2085, 0x2086, 0x2087, 0x2088, 0x2089, 0x208a, 0x208b, + 0x208c, 0x208d, 0x208e, 0x208f, 0x2090, 0x2091, 0x2092, 0x2093, + 0x2094, 0x2095, 0x2096, 0x2097, 0x2098, 0x2099, 0x209a, 0x209b, + 0x209c, 0x209d, 0x209e, 0x209f, 0x20a0, 0x20a1, 0x20a2, 0x20a3, + 0x20a4, 0x20a5, 0x20a6, 0x20a7, 0x20a8, 0x20a9, 0x20aa, 0x20ab, + 0x20ac, 0x20ad, 0x20ae, 0x20af, 0x20b0, 0x20b1, 0x20b2, 0x20b3, + 0x20b4, 0x20b5, 0x20b6, 0x20b7, 0x20b8, 0x20b9, 0x20ba, 0x20bb, + 0x20bc, 0x20bd, 0x20be, 0x20bf, 0x20c0, 0x20c1, 0x20c2, 0x20c3, + 0x20c4, 0x20c5, 0x20c6, 0x20c7, 0x20c8, 0x20c9, 0x20ca, 0x20cb, + 0x20cc, 0x20cd, 0x20ce, 0x20cf, 0x20d0, 0x20d1, 0x20d2, 0x20d3, + 0x20d4, 0x20d5, 0x20d6, 0x20d7, 0x20d8, 0x20d9, 0x20da, 0x20db, + 0x20dc, 0x20dd, 0x20de, 0x20df, 0x20e0, 0x20e1, 0x20e2, 0x20e3, + 0x20e4, 0x20e5, 0x20e6, 0x20e7, 0x20e8, 0x20e9, 0x20ea, 0x20eb, + 0x20ec, 0x20ed, 0x20ee, 0x20ef, 0x20f0, 0x20f1, 0x20f2, 0x20f3, + 0x20f4, 0x20f5, 0x20f6, 0x20f7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, + 0x20fc, 0x20fd, 0x20fe, 0x20ff, 0x2100, 0x2101, 0x2102, 0x2103, + 0x2104, 0x2105, 0x2106, 0x2107, 0x2108, 0x2109, 0x210a, 0x210b, + 0x210c, 0x210d, 0x210e, 0x210f, 0x2110, 0x2111, 0x2112, 0x2113, + 0x2114, 0x2115, 0x2116, 0x2117, 0x2118, 0x2119, 0x211a, 0x211b, + 0x211c, 0x211d, 0x211e, 0x211f, 0x2120, 0x2121, 0x2122, 0x2123, + 0x2124, 0x2125, 0x2126, 0x2127, 0x2128, 0x2129, 0x212a, 0x212b, + 0x212c, 0x212d, 0x212e, 0x212f, 0x2130, 0x2131, 0x2132, 0x2133, + 0x2134, 0x2135, 0x2136, 0x2137, 0x2138, 0x2139, 0x213a, 0x213b, + 0x213c, 0x213d, 0x213e, 0x213f, 0x2140, 0x2141, 0x2142, 0x2143, + 0x2144, 0x2145, 0x2146, 0x2147, 0x2148, 0x2149, 0x214a, 0x214b, + 0x214c, 0x214d, 0x2132, 0x214f, 0x2150, 0x2151, 0x2152, 0x2153, + 0x2154, 0x2155, 0x2156, 0x2157, 0x2158, 0x2159, 0x215a, 0x215b, + 0x215c, 0x215d, 0x215e, 0x215f, 0x2160, 0x2161, 0x2162, 0x2163, + 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b, + 0x216c, 0x216d, 0x216e, 0x216f, 0x2160, 0x2161, 0x2162, 0x2163, + 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216a, 0x216b, + 0x216c, 0x216d, 0x216e, 0x216f, 0x2180, 0x2181, 0x2182, 0x2183, + 0x2183, 0xffff, 0x034b, 0x24b6, 0x24b7, 0x24b8, 0x24b9, 0x24ba, + 0x24bb, 0x24bc, 0x24bd, 0x24be, 0x24bf, 0x24c0, 0x24c1, 0x24c2, + 0x24c3, 0x24c4, 0x24c5, 0x24c6, 0x24c7, 0x24c8, 0x24c9, 0x24ca, + 0x24cb, 0x24cc, 0x24cd, 0x24ce, 0x24cf, 0xffff, 0x0746, 0x2c00, + 0x2c01, 0x2c02, 0x2c03, 0x2c04, 0x2c05, 0x2c06, 0x2c07, 0x2c08, + 0x2c09, 0x2c0a, 0x2c0b, 0x2c0c, 0x2c0d, 0x2c0e, 0x2c0f, 0x2c10, + 0x2c11, 0x2c12, 0x2c13, 0x2c14, 0x2c15, 0x2c16, 0x2c17, 0x2c18, + 0x2c19, 0x2c1a, 0x2c1b, 0x2c1c, 0x2c1d, 0x2c1e, 0x2c1f, 0x2c20, + 0x2c21, 0x2c22, 0x2c23, 0x2c24, 0x2c25, 0x2c26, 0x2c27, 0x2c28, + 0x2c29, 0x2c2a, 0x2c2b, 0x2c2c, 0x2c2d, 0x2c2e, 0x2c5f, 0x2c60, + 0x2c60, 0x2c62, 0x2c63, 0x2c64, 0x2c65, 0x2c66, 0x2c67, 0x2c67, + 0x2c69, 0x2c69, 0x2c6b, 0x2c6b, 0x2c6d, 0x2c6e, 0x2c6f, 0x2c70, + 0x2c71, 0x2c72, 0x2c73, 0x2c74, 0x2c75, 0x2c75, 0x2c77, 0x2c78, + 0x2c79, 0x2c7a, 0x2c7b, 0x2c7c, 0x2c7d, 0x2c7e, 0x2c7f, 0x2c80, + 0x2c80, 0x2c82, 0x2c82, 0x2c84, 0x2c84, 0x2c86, 0x2c86, 0x2c88, + 0x2c88, 0x2c8a, 0x2c8a, 0x2c8c, 0x2c8c, 0x2c8e, 0x2c8e, 0x2c90, + 0x2c90, 0x2c92, 0x2c92, 0x2c94, 0x2c94, 0x2c96, 0x2c96, 0x2c98, + 0x2c98, 0x2c9a, 0x2c9a, 0x2c9c, 0x2c9c, 0x2c9e, 0x2c9e, 0x2ca0, + 0x2ca0, 0x2ca2, 0x2ca2, 0x2ca4, 0x2ca4, 0x2ca6, 0x2ca6, 0x2ca8, + 0x2ca8, 0x2caa, 0x2caa, 0x2cac, 0x2cac, 0x2cae, 0x2cae, 0x2cb0, + 0x2cb0, 0x2cb2, 0x2cb2, 0x2cb4, 0x2cb4, 0x2cb6, 0x2cb6, 0x2cb8, + 0x2cb8, 0x2cba, 0x2cba, 0x2cbc, 0x2cbc, 0x2cbe, 0x2cbe, 0x2cc0, + 0x2cc0, 0x2cc2, 0x2cc2, 0x2cc4, 0x2cc4, 0x2cc6, 0x2cc6, 0x2cc8, + 0x2cc8, 0x2cca, 0x2cca, 0x2ccc, 0x2ccc, 0x2cce, 0x2cce, 0x2cd0, + 0x2cd0, 0x2cd2, 0x2cd2, 0x2cd4, 0x2cd4, 0x2cd6, 0x2cd6, 0x2cd8, + 0x2cd8, 0x2cda, 0x2cda, 0x2cdc, 0x2cdc, 0x2cde, 0x2cde, 0x2ce0, + 0x2ce0, 0x2ce2, 0x2ce2, 0x2ce4, 0x2ce5, 0x2ce6, 0x2ce7, 0x2ce8, + 0x2ce9, 0x2cea, 0x2ceb, 0x2cec, 0x2ced, 0x2cee, 0x2cef, 0x2cf0, + 0x2cf1, 0x2cf2, 0x2cf3, 0x2cf4, 0x2cf5, 0x2cf6, 0x2cf7, 0x2cf8, + 0x2cf9, 0x2cfa, 0x2cfb, 0x2cfc, 0x2cfd, 0x2cfe, 0x2cff, 0x10a0, + 0x10a1, 0x10a2, 0x10a3, 0x10a4, 0x10a5, 0x10a6, 0x10a7, 0x10a8, + 0x10a9, 0x10aa, 0x10ab, 0x10ac, 0x10ad, 0x10ae, 0x10af, 0x10b0, + 0x10b1, 0x10b2, 0x10b3, 0x10b4, 0x10b5, 0x10b6, 0x10b7, 0x10b8, + 0x10b9, 0x10ba, 0x10bb, 0x10bc, 0x10bd, 0x10be, 0x10bf, 0x10c0, + 0x10c1, 0x10c2, 0x10c3, 0x10c4, 0x10c5, 0xffff, 0xd21b, 0xff21, + 0xff22, 0xff23, 0xff24, 0xff25, 0xff26, 0xff27, 0xff28, 0xff29, + 0xff2a, 0xff2b, 0xff2c, 0xff2d, 0xff2e, 0xff2f, 0xff30, 0xff31, + 0xff32, 0xff33, 0xff34, 0xff35, 0xff36, 0xff37, 0xff38, 0xff39, + 0xff3a, 0xff5b, 0xff5c, 0xff5d, 0xff5e, 0xff5f, 0xff60, 0xff61, + 0xff62, 0xff63, 0xff64, 0xff65, 0xff66, 0xff67, 0xff68, 0xff69, + 0xff6a, 0xff6b, 0xff6c, 0xff6d, 0xff6e, 0xff6f, 0xff70, 0xff71, + 0xff72, 0xff73, 0xff74, 0xff75, 0xff76, 0xff77, 0xff78, 0xff79, + 0xff7a, 0xff7b, 0xff7c, 0xff7d, 0xff7e, 0xff7f, 0xff80, 0xff81, + 0xff82, 0xff83, 0xff84, 0xff85, 0xff86, 0xff87, 0xff88, 0xff89, + 0xff8a, 0xff8b, 0xff8c, 0xff8d, 0xff8e, 0xff8f, 0xff90, 0xff91, + 0xff92, 0xff93, 0xff94, 0xff95, 0xff96, 0xff97, 0xff98, 0xff99, + 0xff9a, 0xff9b, 0xff9c, 0xff9d, 0xff9e, 0xff9f, 0xffa0, 0xffa1, + 0xffa2, 0xffa3, 0xffa4, 0xffa5, 0xffa6, 0xffa7, 0xffa8, 0xffa9, + 0xffaa, 0xffab, 0xffac, 0xffad, 0xffae, 0xffaf, 0xffb0, 0xffb1, + 0xffb2, 0xffb3, 0xffb4, 0xffb5, 0xffb6, 0xffb7, 0xffb8, 0xffb9, + 0xffba, 0xffbb, 0xffbc, 0xffbd, 0xffbe, 0xffbf, 0xffc0, 0xffc1, + 0xffc2, 0xffc3, 0xffc4, 0xffc5, 0xffc6, 0xffc7, 0xffc8, 0xffc9, + 0xffca, 0xffcb, 0xffcc, 0xffcd, 0xffce, 0xffcf, 0xffd0, 0xffd1, + 0xffd2, 0xffd3, 0xffd4, 0xffd5, 0xffd6, 0xffd7, 0xffd8, 0xffd9, + 0xffda, 0xffdb, 0xffdc, 0xffdd, 0xffde, 0xffdf, 0xffe0, 0xffe1, + 0xffe2, 0xffe3, 0xffe4, 0xffe5, 0xffe6, 0xffe7, 0xffe8, 0xffe9, + 0xffea, 0xffeb, 0xffec, 0xffed, 0xffee, 0xffef, 0xfff0, 0xfff1, + 0xfff2, 0xfff3, 0xfff4, 0xfff5, 0xfff6, 0xfff7, 0xfff8, 0xfff9, + 0xfffa, 0xfffb, 0xfffc, 0xfffd, 0xfffe, 0xffff, +}; + +/* + * Allow full-width illegal characters : + * "MS windows 7" supports full-width-invalid-name-characters. + * So we should check half-width-invalid-name-characters(ASCII) only + * for compatibility. + * + * " * / : < > ? \ | + * + * patch 1.2.0 + */ +static unsigned short bad_uni_chars[] = { + 0x0022, 0x002A, 0x002F, 0x003A, + 0x003C, 0x003E, 0x003F, 0x005C, 0x007C, + 0 +}; + +static int exfat_convert_ch_to_uni(struct nls_table *nls, + const unsigned char *ch, unsigned short *uni, int *lossy) +{ + int len; + + *uni = 0x0; + + if (ch[0] < 0x80) { + *uni = ch[0]; + return 1; + } + + len = nls->char2uni(ch, MAX_CHARSET_SIZE, uni); + if (len < 0) { + /* conversion failed */ + if (lossy != NULL) + *lossy |= NLS_NAME_LOSSY; + *uni = '_'; + if (!strcmp(nls->charset, "utf8")) + return 1; + return 2; + } + + return len; +} + +static int exfat_convert_uni_to_ch(struct nls_table *nls, unsigned short uni, + unsigned char *ch, int *lossy) +{ + int len; + + ch[0] = 0x0; + + if (uni < 0x0080) { + ch[0] = uni; + return 1; + } + + len = nls->uni2char(uni, ch, MAX_CHARSET_SIZE); + if (len < 0) { + /* conversion failed */ + if (lossy != NULL) + *lossy |= NLS_NAME_LOSSY; + ch[0] = '_'; + return 1; + } + + return len; +} + +static unsigned short exfat_nls_upper(struct super_block *sb, unsigned short a) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + if (!sbi->options.case_sensitive && sbi->vol_utbl[a]) + return sbi->vol_utbl[a]; + + return a; +} + +static unsigned short *exfat_nls_wstrchr(unsigned short *str, + unsigned short wchar) +{ + while (*str) { + if (*(str++) == wchar) + return str; + } + + return NULL; +} + +int exfat_nls_cmp_uniname(struct super_block *sb, unsigned short *a, + unsigned short *b) +{ + int i; + + for (i = 0; i < MAX_NAME_LENGTH; i++, a++, b++) { + if (exfat_nls_upper(sb, *a) != exfat_nls_upper(sb, *b)) + return 1; + if (*a == 0x0) + return 0; + } + return 0; +} + +static int __exfat_nls_utf16s_to_vfsname(struct super_block *sb, + struct exfat_uni_name *p_uniname, unsigned char *p_cstring, + int buflen) +{ + int len; + const unsigned short *uniname = p_uniname->name; + + /* always len >= 0 */ + len = utf16s_to_utf8s(uniname, MAX_NAME_LENGTH, UTF16_HOST_ENDIAN, + p_cstring, buflen); + p_cstring[len] = '\0'; + return len; +} + +static int __exfat_nls_vfsname_to_utf16s(struct super_block *sb, + const unsigned char *p_cstring, const int len, + struct exfat_uni_name *p_uniname, int *p_lossy) +{ + int i, unilen, lossy = NLS_NAME_NO_LOSSY; + unsigned short upname[MAX_NAME_LENGTH + 1]; + unsigned short *uniname = p_uniname->name; + + WARN_ON(!len); + + unilen = utf8s_to_utf16s(p_cstring, len, UTF16_HOST_ENDIAN, + (wchar_t *)uniname, MAX_NAME_LENGTH + 2); + if (unilen < 0) { + exfat_msg(sb, KERN_ERR, + "failed to vfsname_to_utf16(err : %d) vfsnamelen : %d", + unilen, len); + return unilen; + } + + if (unilen > MAX_NAME_LENGTH) { + exfat_msg(sb, KERN_ERR, + "failed to vfsname_to_utf16(estr:ENAMETOOLONG) vfsnamelen : %d, unilen : %d > %d", + len, unilen, MAX_NAME_LENGTH); + return -ENAMETOOLONG; + } + + p_uniname->name_len = unilen & 0xFF; + + for (i = 0; i < unilen; i++) { + if (*uniname < 0x0020 || + exfat_nls_wstrchr(bad_uni_chars, *uniname)) + lossy |= NLS_NAME_LOSSY; + + upname[i] = exfat_nls_upper(sb, *uniname); + uniname++; + } + + *uniname = '\0'; + p_uniname->name_len = unilen; + p_uniname->name_hash = exfat_calc_chksum_2byte(upname, unilen << 1, 0, + CS_DEFAULT); + + if (p_lossy) + *p_lossy = lossy; + + return unilen; +} + +static int __exfat_nls_uni16s_to_vfsname(struct super_block *sb, + struct exfat_uni_name *p_uniname, unsigned char *p_cstring, + int buflen) +{ + int i, j, len, out_len = 0; + unsigned char buf[MAX_CHARSET_SIZE]; + const unsigned short *uniname = p_uniname->name; + struct nls_table *nls = EXFAT_SB(sb)->nls_io; + + i = 0; + while (i < MAX_NAME_LENGTH && out_len < (buflen - 1)) { + if (*uniname == '\0') + break; + + len = exfat_convert_uni_to_ch(nls, *uniname, buf, NULL); + if (out_len + len >= buflen) + len = buflen - 1 - out_len; + out_len += len; + + if (len > 1) { + for (j = 0; j < len; j++) + *p_cstring++ = buf[j]; + } else { /* len == 1 */ + *p_cstring++ = *buf; + } + + uniname++; + i++; + } + + *p_cstring = '\0'; + return out_len; +} + +static int __exfat_nls_vfsname_to_uni16s(struct super_block *sb, + const unsigned char *p_cstring, const int len, + struct exfat_uni_name *p_uniname, int *p_lossy) +{ + int i = 0, unilen = 0, lossy = NLS_NAME_NO_LOSSY; + unsigned short upname[MAX_NAME_LENGTH + 1]; + unsigned short *uniname = p_uniname->name; + struct nls_table *nls = EXFAT_SB(sb)->nls_io; + + WARN_ON(!len); + + while (unilen < MAX_NAME_LENGTH && i < len) { + i += exfat_convert_ch_to_uni(nls, p_cstring + i, uniname, + &lossy); + + if (*uniname < 0x0020 || + exfat_nls_wstrchr(bad_uni_chars, *uniname)) + lossy |= NLS_NAME_LOSSY; + + upname[unilen] = exfat_nls_upper(sb, *uniname); + uniname++; + unilen++; + } + + if (p_cstring[i] != '\0') + lossy |= NLS_NAME_OVERLEN; + + *uniname = '\0'; + p_uniname->name_len = unilen; + p_uniname->name_hash = exfat_calc_chksum_2byte(upname, unilen << 1, 0, + CS_DEFAULT); + + if (p_lossy) + *p_lossy = lossy; + + return unilen; +} + +int exfat_nls_uni16s_to_vfsname(struct super_block *sb, + struct exfat_uni_name *uniname, unsigned char *p_cstring, + int buflen) +{ + if (EXFAT_SB(sb)->options.utf8) + return __exfat_nls_utf16s_to_vfsname(sb, uniname, p_cstring, + buflen); + return __exfat_nls_uni16s_to_vfsname(sb, uniname, p_cstring, buflen); +} + +int exfat_nls_vfsname_to_uni16s(struct super_block *sb, + const unsigned char *p_cstring, const int len, + struct exfat_uni_name *uniname, int *p_lossy) +{ + if (EXFAT_SB(sb)->options.utf8) + return __exfat_nls_vfsname_to_utf16s(sb, p_cstring, len, + uniname, p_lossy); + return __exfat_nls_vfsname_to_uni16s(sb, p_cstring, len, uniname, + p_lossy); +} + +static int exfat_load_upcase_table(struct super_block *sb, + sector_t sector, unsigned long long num_sectors, + unsigned int utbl_checksum) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bh = NULL; + unsigned int sect_size = sb->s_blocksize; + unsigned int i, index = 0, checksum = 0; + int ret = -EIO; + unsigned char skip = false; + unsigned short *upcase_table; + + upcase_table = kcalloc(UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL); + if (!upcase_table) + return -ENOMEM; + + sbi->vol_utbl = upcase_table; + num_sectors += sector; + + while (sector < num_sectors) { + bh = sb_bread(sb, sector); + if (!bh) { + exfat_msg(sb, KERN_ERR, + "failed to read sector(0x%llx)\n", sector); + goto error; + } + sector++; + for (i = 0; i < sect_size && index <= 0xFFFF; i += 2) { + unsigned short uni = get_unaligned_le16(bh->b_data + i); + + checksum = ((checksum & 1) ? 0x80000000 : 0) + + (checksum >> 1) + + *(((unsigned char *)bh->b_data) + i); + checksum = ((checksum & 1) ? 0x80000000 : 0) + + (checksum >> 1) + + *(((unsigned char *)bh->b_data) + (i + 1)); + + if (skip) { + index += uni; + skip = false; + } else if (uni == index) { + index++; + } else if (uni == 0xFFFF) { + skip = true; + } else { /* uni != index , uni != 0xFFFF */ + upcase_table[index] = uni; + index++; + } + } + } + + if (index >= 0xFFFF && utbl_checksum == checksum) { + if (bh) + brelse(bh); + return 0; + } + + exfat_msg(sb, KERN_ERR, + "failed to load upcase table (idx : 0x%08x, chksum : 0x%08x, utbl_chksum : 0x%08x)\n", + index, checksum, utbl_checksum); + + ret = -EINVAL; +error: + if (bh) + brelse(bh); + exfat_free_upcase_table(sb); + return ret; +} + +static int exfat_load_default_upcase_table(struct super_block *sb) +{ + int i, ret = -EIO; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + unsigned char skip = false; + unsigned short uni = 0, *upcase_table; + unsigned int index = 0; + + upcase_table = kcalloc(UTBL_COUNT, sizeof(unsigned short), GFP_KERNEL); + if (!upcase_table) + return -ENOMEM; + + sbi->vol_utbl = upcase_table; + + for (i = 0; index <= 0xFFFF && i < EXFAT_NUM_UPCASE; i++) { + uni = uni_def_upcase[i]; + if (skip) { + index += uni; + skip = false; + } else if (uni == index) { + index++; + } else if (uni == 0xFFFF) { + skip = true; + } else { + upcase_table[index] = uni; + index++; + } + } + + if (index >= 0xFFFF) + return 0; + + /* FATAL error: default upcase table has error */ + exfat_free_upcase_table(sb); + return ret; +} + +int exfat_create_upcase_table(struct super_block *sb) +{ + int i, ret; + unsigned int tbl_clu, type; + sector_t sector; + unsigned long long tbl_size, num_sectors; + unsigned char blksize_bits = sb->s_blocksize_bits; + struct exfat_chain clu; + struct exfat_dentry *ep; + struct exfat_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bh; + + clu.dir = sbi->root_dir; + clu.flags = 0x01; + + while (clu.dir != EOF_CLUSTER) { + for (i = 0; i < sbi->dentries_per_clu; i++) { + ep = exfat_get_dentry(sb, &clu, i, &bh, NULL); + if (!ep) + return -EIO; + + type = exfat_get_entry_type(ep); + if (type == TYPE_UNUSED) { + brelse(bh); + break; + } + + if (type != TYPE_UPCASE) { + brelse(bh); + continue; + } + + tbl_clu = le32_to_cpu(ep->upcase_start_clu); + tbl_size = le64_to_cpu(ep->upcase_size); + + sector = exfat_cluster_to_sector(sbi, tbl_clu); + num_sectors = ((tbl_size - 1) >> blksize_bits) + 1; + ret = exfat_load_upcase_table(sb, sector, num_sectors, + le32_to_cpu(ep->upcase_checksum)); + + brelse(bh); + if (ret && ret != -EIO) + goto load_default; + + /* load successfully */ + return ret; + } + + if (exfat_get_next_cluster(sb, &(clu.dir))) + return -EIO; + } + +load_default: + /* load default upcase table */ + return exfat_load_default_upcase_table(sb); +} + +void exfat_free_upcase_table(struct super_block *sb) +{ + struct exfat_sb_info *sbi = EXFAT_SB(sb); + + kfree(sbi->vol_utbl); + sbi->vol_utbl = NULL; +} From patchwork Tue Nov 19 09:37:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11251621 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id AE4556C1 for ; Tue, 19 Nov 2019 09:40:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 84F812230E for ; Tue, 19 Nov 2019 09:40:36 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="gOxU8gkS" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727664AbfKSJkd (ORCPT ); Tue, 19 Nov 2019 04:40:33 -0500 Received: from mailout3.samsung.com ([203.254.224.33]:28273 "EHLO mailout3.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726510AbfKSJkd (ORCPT ); Tue, 19 Nov 2019 04:40:33 -0500 Received: from epcas1p1.samsung.com (unknown [182.195.41.45]) by mailout3.samsung.com (KnoxPortal) with ESMTP id 20191119094030epoutp03511ed30ba0f4a68a1c3cf721441984d4~Yhu74cXJo1838918389epoutp033 for ; Tue, 19 Nov 2019 09:40:30 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout3.samsung.com 20191119094030epoutp03511ed30ba0f4a68a1c3cf721441984d4~Yhu74cXJo1838918389epoutp033 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574156430; bh=W/nvY8vh7b5VKEGm7/5P+NKNPdIjQbHkTzk7gijW0ro=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=gOxU8gkSqCmuaDTzHij2kzAvzVbLJTCAdlynvCC8Ez2/t2NjkVurNSiLEWJGC5ghz bBHjfFFViaoZ8xrTZzFNreJZs/PXMl4zQ3laQ3wN6r3Aos2b94QHgEUDh75ibifPCA T2QWwd1MxTJqgqTE4ekbmWO4w9OMR1Y5hPC3lTBQ= Received: from epsnrtp1.localdomain (unknown [182.195.42.162]) by epcas1p2.samsung.com (KnoxPortal) with ESMTP id 20191119094030epcas1p2612217083e19225efdd6fd8be210d1f1~Yhu7cYV4l0375303753epcas1p22; Tue, 19 Nov 2019 09:40:30 +0000 (GMT) Received: from epsmges1p3.samsung.com (unknown [182.195.40.163]) by epsnrtp1.localdomain (Postfix) with ESMTP id 47HLR01HwdzMqYll; Tue, 19 Nov 2019 09:40:28 +0000 (GMT) Received: from epcas1p4.samsung.com ( [182.195.41.48]) by epsmges1p3.samsung.com (Symantec Messaging Gateway) with SMTP id BE.07.04080.B88B3DD5; Tue, 19 Nov 2019 18:40:27 +0900 (KST) Received: from epsmtrp2.samsung.com (unknown [182.195.40.14]) by epcas1p4.samsung.com (KnoxPortal) with ESMTPA id 20191119094026epcas1p4bee051f56352505eab919011ec740c5d~Yhu4RAffa0056700567epcas1p4m; Tue, 19 Nov 2019 09:40:26 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp2.samsung.com (KnoxPortal) with ESMTP id 20191119094026epsmtrp244743dfa374fe7756d0f42f594ff68de~Yhu4QQuI50322403224epsmtrp2f; Tue, 19 Nov 2019 09:40:26 +0000 (GMT) X-AuditID: b6c32a37-7cdff70000000ff0-c6-5dd3b88b1c39 Received: from epsmtip2.samsung.com ( [182.195.34.31]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 16.12.03654.A88B3DD5; Tue, 19 Nov 2019 18:40:26 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20191119094026epsmtip26cc73e231a4e2faa3b695b1a0be389c7~Yhu4HBDA10817608176epsmtip2E; Tue, 19 Nov 2019 09:40:26 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, Namjae Jeon Subject: [PATCH v3 11/13] exfat: add Kconfig and Makefile Date: Tue, 19 Nov 2019 04:37:16 -0500 Message-Id: <20191119093718.3501-12-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119093718.3501-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFmpmk+LIzCtJLcpLzFFi42LZdljTQLd7x+VYg3V3WS0OP57EbtG8eD2b xcrVR5ksrt+9xWyxZ+9JFovLu+awWfyf9ZzV4sf0eost/46wWlx6/4HFgctj56y77B77565h 99h9s4HNo2/LKkaPzaerPT5vkvM4tP0Nm8ftZ9tYAjiicmwyUhNTUosUUvOS81My89JtlbyD 453jTc0MDHUNLS3MlRTyEnNTbZVcfAJ03TJzgE5UUihLzCkFCgUkFhcr6dvZFOWXlqQqZOQX l9gqpRak5BQYGhToFSfmFpfmpesl5+daGRoYGJkCVSbkZBx541nQwVsx4U8zSwPjJa4uRk4O CQETieOLJzB1MXJxCAnsYJTY/+MzlPOJUeLg7DnMEM43Ron5y18wwrS0vu5kgUjsZZT4cGYR Qsv6ybOAMhwcbALaEn+2iII0iAjYS2yefQCsgVngCKPEiy8T2EFqhAUsJTZMcgGpYRFQlbj9 6y/YAl4BG4nOvStZIJbJS6zecIAZxOYEij+cvYwRZI6EwBE2icZr69kgilwkJmw5zgphC0u8 Or6FHcKWkvj8bi8byC4JgWqJj/uZIcIdQDd8t4WwjSVurt/AClLCLKApsX6XPkRYUWLn77lg 5zAL8Em8+9rDCjGFV6KjTQiiRFWi79JhJghbWqKr/QM7RImHxN05XpAA6WeU+LznAdMERrlZ CAsWMDKuYhRLLSjOTU8tNiwwRo6uTYzgBKhlvoNxwzmfQ4wCHIxKPLwK6pdjhVgTy4orcw8x SnAwK4nw+j26ECvEm5JYWZValB9fVJqTWnyI0RQYjhOZpUST84HJOa8k3tDUyNjY2MLEzNzM 1FhJnJfjx8VYIYH0xJLU7NTUgtQimD4mDk6pBkY1/az6I2mucy58PWDQfDxrU8y2qKo1Ezt/ B5g52lqUGCWr7BZolAi4tO9s5r5u+1eH966+/3yH8oz5LdsXysatW7Rm921dReePm9f01Pyq beuKa3JPPbjQTXmpbKbInfdOky42//ohqNGWbNQecfDgDq7MS2HH94qrnmJ9m2ZsOX+FGl/w u8NKLMUZiYZazEXFiQDv4GRslgMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrLLMWRmVeSWpSXmKPExsWy7bCSvG7XjsuxBl836VocfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9RZb/h1htbj0/gOLA5fHzll32T32z13D 7rH7ZgObR9+WVYwem09Xe3zeJOdxaPsbNo/bz7axBHBEcdmkpOZklqUW6dslcGUceeNZ0MFb MeFPM0sD4yWuLkZODgkBE4nW150sXYxcHEICuxkldk99xwKRkJY4duIMcxcjB5AtLHH4cDFE zQdGiTVbJoPF2QS0Jf5sEQUpFxFwlOjddRhsDrPAOUaJnc+WMYLUCAtYSmyY5AJSwyKgKnH7 119GEJtXwEaic+9KqFXyEqs3HGAGsTmB4g9nLwOrERKwlmh81Mw+gZFvASPDKkbJ1ILi3PTc YsMCw7zUcr3ixNzi0rx0veT83E2M4EDV0tzBeHlJ/CFGAQ5GJR7eEyqXY4VYE8uKK3MPMUpw MCuJ8Po9uhArxJuSWFmVWpQfX1Sak1p8iFGag0VJnPdp3rFIIYH0xJLU7NTUgtQimCwTB6dU A2NsWH5hc9k7wcXTGf12u9psTPx7+dF254N5ySl1Z6IWiZU/E6/g2iUfct/hDpumqFqMa56y 27HaScdrhZRtDLOFJCprnXflnpO8PLdYuuTEH6vy5I0nnL6uLeP9J8y4UuO+t4udk9iHn1vl 4mqDHt7sFna1rpEXs1/05dqMrHsem899/awnqcRSnJFoqMVcVJwIAHdZpuZQAgAA X-CMS-MailID: 20191119094026epcas1p4bee051f56352505eab919011ec740c5d X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119094026epcas1p4bee051f56352505eab919011ec740c5d References: <20191119093718.3501-1-namjae.jeon@samsung.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org This adds the Kconfig and Makefile for exfat. Signed-off-by: Namjae Jeon Signed-off-by: Sungjong Seo --- fs/exfat/Kconfig | 21 +++++++++++++++++++++ fs/exfat/Makefile | 8 ++++++++ 2 files changed, 29 insertions(+) create mode 100644 fs/exfat/Kconfig create mode 100644 fs/exfat/Makefile diff --git a/fs/exfat/Kconfig b/fs/exfat/Kconfig new file mode 100644 index 000000000000..11d841a5f7f0 --- /dev/null +++ b/fs/exfat/Kconfig @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config EXFAT + tristate "exFAT filesystem support" + select NLS + help + This allows you to mount devices formatted with the exFAT file system. + exFAT is typically used on SD-Cards or USB sticks. + + To compile this as a module, choose M here: the module will be called + exfat. + +config EXFAT_FS_DEFAULT_IOCHARSET + string "Default iocharset for exFAT" + default "utf8" + depends on EXFAT + help + Set this to the default input/output character set you'd + like exFAT to use. It should probably match the character set + that most of your exFAT filesystems use, and can be overridden + with the "iocharset" mount option for exFAT filesystems. diff --git a/fs/exfat/Makefile b/fs/exfat/Makefile new file mode 100644 index 000000000000..e9193346c80c --- /dev/null +++ b/fs/exfat/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Makefile for the linux exFAT filesystem support. +# +obj-$(CONFIG_EXFAT) += exfat.o + +exfat-y := inode.o namei.o dir.o super.o fatent.o cache.o nls.o misc.o \ + file.o balloc.o From patchwork Tue Nov 19 09:37:17 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11251627 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 36A576C1 for ; Tue, 19 Nov 2019 09:40:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 0C74622304 for ; Tue, 19 Nov 2019 09:40:51 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="mqP6bN/L" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727716AbfKSJkr (ORCPT ); Tue, 19 Nov 2019 04:40:47 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:14102 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727615AbfKSJkc (ORCPT ); Tue, 19 Nov 2019 04:40:32 -0500 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20191119094029epoutp042c8ef76c3ebee9e0a8dc24a3f4e117e8~Yhu66C1ey0888908889epoutp04W for ; Tue, 19 Nov 2019 09:40:29 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20191119094029epoutp042c8ef76c3ebee9e0a8dc24a3f4e117e8~Yhu66C1ey0888908889epoutp04W DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574156429; bh=LzsUvoRQ+/EvYb4sCIgQeQ8VbnU5fmhs4hyN/5tdGrs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=mqP6bN/L1cCNM6MuzQyTUdWtFJ2gQvtjsOzmG5ggwuXHLgEgwPfYr9VQPWvIBBq/2 eL4AVzSvReeJC3VPHb95Oi1uuQjRbQ8m2B8V59RmSrFptMa/0Du3tXjP0W7F14rKsL p2+UCwJvFBwmWMmaEl+ojg6sDIyTfA+O7wROntXo= Received: from epsnrtp3.localdomain (unknown [182.195.42.164]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20191119094028epcas1p16ea6bbbcc65534c8c7976482a703e7c9~Yhu6RQrhu0953509535epcas1p13; Tue, 19 Nov 2019 09:40:28 +0000 (GMT) Received: from epsmges1p4.samsung.com (unknown [182.195.40.162]) by epsnrtp3.localdomain (Postfix) with ESMTP id 47HLR00HGBzMqYkb; Tue, 19 Nov 2019 09:40:28 +0000 (GMT) Received: from epcas1p4.samsung.com ( [182.195.41.48]) by epsmges1p4.samsung.com (Symantec Messaging Gateway) with SMTP id 24.A4.04406.B88B3DD5; Tue, 19 Nov 2019 18:40:27 +0900 (KST) Received: from epsmtrp2.samsung.com (unknown [182.195.40.14]) by epcas1p4.samsung.com (KnoxPortal) with ESMTPA id 20191119094027epcas1p416eca98bc8ccefbcada87ae6b475d4c4~Yhu44AOD92221122211epcas1p4Z; Tue, 19 Nov 2019 09:40:27 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp2.samsung.com (KnoxPortal) with ESMTP id 20191119094027epsmtrp2ce954b0514e369a1ca8cc7e1d46b697d~Yhu43Wbrl0322303223epsmtrp2j; Tue, 19 Nov 2019 09:40:27 +0000 (GMT) X-AuditID: b6c32a38-947ff70000001136-41-5dd3b88be542 Received: from epsmtip2.samsung.com ( [182.195.34.31]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id 20.A2.03814.B88B3DD5; Tue, 19 Nov 2019 18:40:27 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20191119094027epsmtip2d6737fe0092ba5d731068cce22ee09f5~Yhu4sxHwG0597405974epsmtip2f; Tue, 19 Nov 2019 09:40:27 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, Namjae Jeon Subject: [PATCH v3 12/13] exfat: add exfat in fs/Kconfig and fs/Makefile Date: Tue, 19 Nov 2019 04:37:17 -0500 Message-Id: <20191119093718.3501-13-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119093718.3501-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA01Sa0hTYRjm29nOztTVaWV9LDE7tMBC29rFk2gFRZxIRIiCIrGDO+hoN87Z LDVokHjLLAsytYsaM9JMU/NWy3BGRWne6CYSKZRZeMl0hVptO93+PTzv8z7v873fiyGyk6gc M5htDGumjQQaIGx2hysjTrUOJCqLB3Wke/ScmDx5rQ4lb9Q8FJAvh98g5D3XEyE50H4JJX+W fhCR34pPkE0/ukRk/+SUcHsA1VY6LKY6Lt8UU3dfO1CqsKkaUI1PM6mZhlCqs+UzSg29bxYm YAeNMakMrWfYMMacbNEbzCmxxJ69STuStDqlKkK1hYwiwsy0iYkldsYlROwyGL0RibA02mj3 Ugk0xxGbtsawFruNCUu1cLZYgrHqjVaV0hrJ0SbObk6JTLaYolVK5WatV3nYmHpuxgmsVdix h7UtiANMovlAgkFcA3vzvgryQQAmw1sBLDz/DPEVZPgXAO+UM3xhDsCKrLfiPx2jI/VCvuAC MMsxL/jb8epMZD7AMBTfCBeagn30CnwbbCx74NcjeBeAY1/P+o2W4xTMrnT7pwlxBXTn3vT7 SPEYODy4CPhha2BN/QO/RuLl35VVAZ8RxB+j8Lqn5PcbdkJHX66Ix8vh+KOm30nlcGbChfoC QTwTTncgPJ3rDeGJ5bEavq6rF/kkCB4O69o38fRa2DZ/2R8BwZfAidkCEe8ihbnZMl6igIX9 bgGPV8P8nCkxL6FgbZuS384ZALt7s8RnQWjpvwHlAFSDlYyVM6UwnMqq+f+7GoD/ADeQreBe T1wnwDFABEkfrxtIlInoNC7d1AkghhArpPEjvYkyqZ5Oz2BYSxJrNzJcJ9B691iEyIOTLd5z NtuSVNrNarWa1OiidFo1sUqKfetLlOEptI05wjBWhv3TJ8AkcgcIPLrMveaqIl2fNiTJdNqz ivZPbTMoDhXfJjZ6OpzHt06f3idfDIl7VhrtqQxx3mqMx06PBxTkjYVHOEqaNabFo/eDZyuc L3a35EgWxuPWf2SH7F0TW74HDb57r4samnvecCWwvTbD4OqavFjn+FTx9IKnZEx6sLsn+YBN s5RdDCGEXCqt2oCwHP0LbpeWrZYDAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrPLMWRmVeSWpSXmKPExsWy7bCSvG73jsuxBmuWSFocfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9RZb/h1htbj0/gOLA5fHzll32T32z13D 7rH7ZgObR9+WVYwem09Xe3zeJOdxaPsbNo/bz7axBHBEcdmkpOZklqUW6dslcGVM+ryUsWAZ R8XRtduZGxjfs3UxcnJICJhIPH60gaWLkYtDSGA3o8SVZW+ZIBLSEsdOnGHuYuQAsoUlDh8u hqj5wCix7MsLNpA4m4C2xJ8toiDlIgKOEr27DoPNYRY4xyix89kyRpCEsICHRNuiw8wgNouA qsThjjVg83kFbCTuXvnLCLFLXmL1hgNgNZxA8YezIXqFBKwlGh81s09g5FvAyLCKUTK1oDg3 PbfYsMAoL7Vcrzgxt7g0L10vOT93EyM4WLW0djCeOBF/iFGAg1GJh/eEyuVYIdbEsuLK3EOM EhzMSiK8fo8uxArxpiRWVqUW5ccXleakFh9ilOZgURLnlc8/FikkkJ5YkpqdmlqQWgSTZeLg lGpgtLmnbH4mjs170XwNjZvdm2I4uH4+k3Dzej+9JWO/LM/uk5y2EXwz7+ldSrqUJHRuWbbz 1G93L6+z9qzaozljolT8n64pR9r+2rYLNzrLMF5/5VaQn7YlIE+UqUPo6brFxhb2KybxBZm9 u/1w51rJBzwaG8Kn7Fc4tkVs7l/RLaxq7slLGq5vUWIpzkg01GIuKk4EAG/ZLghSAgAA X-CMS-MailID: 20191119094027epcas1p416eca98bc8ccefbcada87ae6b475d4c4 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119094027epcas1p416eca98bc8ccefbcada87ae6b475d4c4 References: <20191119093718.3501-1-namjae.jeon@samsung.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org Add exfat in fs/Kconfig and fs/Makefile. Signed-off-by: Namjae Jeon Signed-off-by: Sungjong Seo --- fs/Kconfig | 3 ++- fs/Makefile | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/Kconfig b/fs/Kconfig index 7b623e9fc1b0..5edd87eb5c13 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -139,9 +139,10 @@ endmenu endif # BLOCK if BLOCK -menu "DOS/FAT/NT Filesystems" +menu "DOS/FAT/EXFAT/NT Filesystems" source "fs/fat/Kconfig" +source "fs/exfat/Kconfig" source "fs/ntfs/Kconfig" endmenu diff --git a/fs/Makefile b/fs/Makefile index 1148c555c4d3..4358dda56b1e 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -83,6 +83,7 @@ obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ obj-$(CONFIG_CODA_FS) += coda/ obj-$(CONFIG_MINIX_FS) += minix/ obj-$(CONFIG_FAT_FS) += fat/ +obj-$(CONFIG_EXFAT) += exfat/ obj-$(CONFIG_BFS_FS) += bfs/ obj-$(CONFIG_ISO9660_FS) += isofs/ obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ From patchwork Tue Nov 19 09:37:18 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11251625 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6D31D6C1 for ; Tue, 19 Nov 2019 09:40:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 4416C22312 for ; Tue, 19 Nov 2019 09:40:46 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="m5Sstefj" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727675AbfKSJkp (ORCPT ); Tue, 19 Nov 2019 04:40:45 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:14118 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727640AbfKSJkd (ORCPT ); Tue, 19 Nov 2019 04:40:33 -0500 Received: from epcas1p1.samsung.com (unknown [182.195.41.45]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20191119094030epoutp04587c567663421d9cd3ef7c0c27fa8cfe~Yhu8EkFKI0888908889epoutp04Y for ; Tue, 19 Nov 2019 09:40:30 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20191119094030epoutp04587c567663421d9cd3ef7c0c27fa8cfe~Yhu8EkFKI0888908889epoutp04Y DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574156430; bh=qOKAH/xLKFcgxQkDaZUSWSDe5GuU0Alfu+G9AfD13f4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=m5SstefjLfER0KVgHoZ4QIpuK4sd/nDB0lr7xzTb3ZYVwz2/OqW+gKCOePopFhsfO 0THhlIHcq9htLDB6PgtDniNQZ1JIt+6CmSDzXiaEbthk025iz9KCRqN1EKvaVgKB/c H21XqWV5MzzwBnXiI5VBADTolHIo2xDmad0hYO4s= Received: from epsnrtp1.localdomain (unknown [182.195.42.162]) by epcas1p2.samsung.com (KnoxPortal) with ESMTP id 20191119094030epcas1p2f80550ea22fa01d8281f6a3a08584ce9~Yhu7kOKRY0594305943epcas1p2B; Tue, 19 Nov 2019 09:40:30 +0000 (GMT) Received: from epsmges1p3.samsung.com (unknown [182.195.40.161]) by epsnrtp1.localdomain (Postfix) with ESMTP id 47HLR15tmFzMqYlv; Tue, 19 Nov 2019 09:40:29 +0000 (GMT) Received: from epcas1p1.samsung.com ( [182.195.41.45]) by epsmges1p3.samsung.com (Symantec Messaging Gateway) with SMTP id 10.17.04080.C88B3DD5; Tue, 19 Nov 2019 18:40:28 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p1.samsung.com (KnoxPortal) with ESMTPA id 20191119094028epcas1p1b69cd151e3e574859a34e3a931d48cf2~Yhu5e1Sbf1202912029epcas1p1n; Tue, 19 Nov 2019 09:40:28 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119094028epsmtrp11752626f62460a50efbd63a3ddfac27e~Yhu5eNoNz0080100801epsmtrp1u; Tue, 19 Nov 2019 09:40:28 +0000 (GMT) X-AuditID: b6c32a37-7cdff70000000ff0-cd-5dd3b88c353a Received: from epsmtip2.samsung.com ( [182.195.34.31]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 37.12.03654.B88B3DD5; Tue, 19 Nov 2019 18:40:28 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip2.samsung.com (KnoxPortal) with ESMTPA id 20191119094027epsmtip26fb07c57a37db5118d506c3c4b73852f~Yhu5RX4eu0708907089epsmtip2I; Tue, 19 Nov 2019 09:40:27 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, Namjae Jeon Subject: [PATCH v3 13/13] MAINTAINERS: add exfat filesystem Date: Tue, 19 Nov 2019 04:37:18 -0500 Message-Id: <20191119093718.3501-14-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119093718.3501-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFmphk+LIzCtJLcpLzFFi42LZdlhTV7dnx+VYgwvnhS0OP57EbtG8eD2b xcrVR5ksrt+9xWyxZ+9JFovLu+awWfyf9ZzV4sf0eost/46wWlx6/4HFgctj56y77B77565h 99h9s4HNo2/LKkaPzaerPT5vkvM4tP0Nm8ftZ9tYAjiicmwyUhNTUosUUvOS81My89JtlbyD 453jTc0MDHUNLS3MlRTyEnNTbZVcfAJ03TJzgE5UUihLzCkFCgUkFhcr6dvZFOWXlqQqZOQX l9gqpRak5BQYGhToFSfmFpfmpesl5+daGRoYGJkCVSbkZFy5spG54Btrxc3GtUwNjA2sXYyc HBICJhIXp8xg62Lk4hAS2MEo8XpSAyOE84lRYvWPfywQzjdGiQO7b7LDtHx9+54JxBYS2Mso Me24BVxHU9MioFkcHGwC2hJ/toiC1IgI2Etsnn0AbBCzwBFGiRdfJoANEhawlth+4BeYzSKg KvFm0huwobwCNhI3P7+Huk9eYvWGA8wgNidQ/OHsZWDnSQicYJNYfuMvI0SRi8ST01OhrhOW eHV8C5QtJfGyv40d5CAJgWqJj/uZIcIdQEd8t4WwjSVurt/AClLCLKApsX6XPkRYUWLn77lg 05kF+CTefe1hhZjCK9HRJgRRoirRd+kwE4QtLdHV/gFqkYfE/ZUOkBDpZ5T4cOw9+wRGuVkI CxYwMq5iFEstKM5NTy02LDBGjq9NjOAUqGW+g3HDOZ9DjAIcjEo8vArql2OFWBPLiitzDzFK cDArifD6PboQK8SbklhZlVqUH19UmpNafIjRFBiOE5mlRJPzgek5ryTe0NTI2NjYwsTM3MzU WEmcl+PHxVghgfTEktTs1NSC1CKYPiYOTqkGRh4+09qznxJVtnK1XXE8PFN0h8sVE7G+2Lc7 fyw/6ly4pk+Vp3H130fMy3dbnfr7Yn9xIlfnJ9Z9Ar8v9TtsfXk8Lyz02flJTRPnVUyf9GGi SX1VlvBBlugfSf7ijmcinzB4mnps6Eq8n/bGRMxJxdBr3447Jju076/tyjUW/FscJR3rryRR osRSnJFoqMVcVJwIAKWEaL+XAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrPLMWRmVeSWpSXmKPExsWy7bCSvG7PjsuxBucuM1ocfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9RZb/h1htbj0/gOLA5fHzll32T32z13D 7rH7ZgObR9+WVYwem09Xe3zeJOdxaPsbNo/bz7axBHBEcdmkpOZklqUW6dslcGVcubKRueAb a8XNxrVMDYwNrF2MnBwSAiYSX9++Z+pi5OIQEtjNKLHxzjpmiIS0xLETZ4BsDiBbWOLw4WKI mg+MEl9OzWQFibMJaEv82SIKUi4i4CjRu+swC0gNs8A5Romdz5YxgiSEBawlth/4xQ5iswio SryZ9IYJxOYVsJG4+fk91BHyEqs3HADbywkUfzgbolcIqLfxUTP7BEa+BYwMqxglUwuKc9Nz iw0LDPNSy/WKE3OLS/PS9ZLzczcxgoNVS3MH4+Ul8YcYBTgYlXh4T6hcjhViTSwrrsw9xCjB wawkwuv36EKsEG9KYmVValF+fFFpTmrxIUZpDhYlcd6neccihQTSE0tSs1NTC1KLYLJMHJxS DYyr0k6Z1vRoBF52tGnLSznavt522YrJc7Xr/bb88l6j0nQ37LML/3rRVy/dKrse39v4ksmY tVGv7WuqvO6n3hWd15g+1FssOdp/bOPdEAlzl/w3ji6fuDe+Fu3vmJReISLhMXX7R5b+zqk+ l/7qPTdeZd0Q2Pjnneql3Sulo4t3H2eccDxM6JISS3FGoqEWc1FxIgAvoKWRUgIAAA== X-CMS-MailID: 20191119094028epcas1p1b69cd151e3e574859a34e3a931d48cf2 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119094028epcas1p1b69cd151e3e574859a34e3a931d48cf2 References: <20191119093718.3501-1-namjae.jeon@samsung.com> Sender: linux-fsdevel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-fsdevel@vger.kernel.org Add myself and Sungjong Seo as exfat maintainer. Signed-off-by: Namjae Jeon Signed-off-by: Sungjong Seo --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 89cb4dd0924d..0001db230e4c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6215,6 +6215,13 @@ F: include/trace/events/mdio.h F: include/uapi/linux/mdio.h F: include/uapi/linux/mii.h +EXFAT FILE SYSTEM +M: Namjae Jeon +M: Sungjong Seo +L: linux-fsdevel@vger.kernel.org +S: Maintained +F: fs/exfat/ + EXFAT FILE SYSTEM M: Valdis Kletnieks L: linux-fsdevel@vger.kernel.org