From patchwork Tue Nov 19 07:10:55 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11250995 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 F2F106C1 for ; Tue, 19 Nov 2019 07:15:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id AFABF2230B for ; Tue, 19 Nov 2019 07:15:03 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="vcvGWZgC" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727884AbfKSHPC (ORCPT ); Tue, 19 Nov 2019 02:15:02 -0500 Received: from mailout2.samsung.com ([203.254.224.25]:62513 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726840AbfKSHOJ (ORCPT ); Tue, 19 Nov 2019 02:14:09 -0500 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20191119071404epoutp020c3e2ecda2afb5bb067321ea8d180e88~YfvFRjBBj1289712897epoutp02n for ; Tue, 19 Nov 2019 07:14:04 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20191119071404epoutp020c3e2ecda2afb5bb067321ea8d180e88~YfvFRjBBj1289712897epoutp02n DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574147644; bh=7WWktjOwzzFi4tqacBsRXJRcgnviLaJJNoXjzq6Hy38=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=vcvGWZgCfsl5EWvNXkvphgpMhw3BzDR7X77GK9zX1LgLPSZsRyZ3nOP/ZgFBVzRgt 9W1Y7zZXHKhxFiLc4lZLA0iLIGzb45a6L5N99mmrEikUoatq/k0o7na2O+VDybIFdS mz3zxrFTdXEmRvVjRJPwIuMg40erfWH/HcPUYOik= Received: from epsnrtp1.localdomain (unknown [182.195.42.162]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20191119071404epcas1p11554f1cc14b27b64c39fba37a370ae45~YfvE6vDLT0158901589epcas1p1m; Tue, 19 Nov 2019 07:14:04 +0000 (GMT) Received: from epsmges1p5.samsung.com (unknown [182.195.40.164]) by epsnrtp1.localdomain (Postfix) with ESMTP id 47HHB31DTmzMqYll; Tue, 19 Nov 2019 07:14:03 +0000 (GMT) Received: from epcas1p2.samsung.com ( [182.195.41.46]) by epsmges1p5.samsung.com (Symantec Messaging Gateway) with SMTP id 02.E8.04237.B3693DD5; Tue, 19 Nov 2019 16:14:03 +0900 (KST) Received: from epsmtrp2.samsung.com (unknown [182.195.40.14]) by epcas1p1.samsung.com (KnoxPortal) with ESMTPA id 20191119071402epcas1p138f426d591ee81b65f45d092bcad0ebc~YfvDVFzZN3222232222epcas1p1s; Tue, 19 Nov 2019 07:14:02 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp2.samsung.com (KnoxPortal) with ESMTP id 20191119071402epsmtrp20898bb2dd9f8fbac0aa723db55de3924~YfvDUR_RO0193901939epsmtrp2K; Tue, 19 Nov 2019 07:14:02 +0000 (GMT) X-AuditID: b6c32a39-913ff7000000108d-08-5dd3963b3ddf Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id B9.84.03814.A3693DD5; Tue, 19 Nov 2019 16:14:02 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191119071402epsmtip1b1e01a0fcff04bf33869c4ffcf41e138~YfvDI90xY1280812808epsmtip1o; Tue, 19 Nov 2019 07:14:02 +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, Namjae Jeon Subject: [PATCH v2 01/13] exfat: add in-memory and on-disk structures and headers Date: Tue, 19 Nov 2019 02:10:55 -0500 Message-Id: <20191119071107.1947-2-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119071107.1947-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrLKsWRmVeSWpSXmKPExsWy7bCmnq71tMuxBmu/c1g0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD0qxyYjNTEltUghNS85PyUzL91WyTs43jne1MzAUNfQ 0sJcSSEvMTfVVsnFJ0DXLTMH6DIlhbLEnFKgUEBicbGSvp1NUX5pSapCRn5xia1SakFKToGh QYFecWJucWleul5yfq6VoYGBkSlQZUJOxr7n69gKNh9grFjwr4W5gfHkfMYuRk4OCQETiUXP W1hAbCGBHYwSR/dHdjFyAdmfGCUa7l9ihXC+MUpMf/UOrmP7+g0sEIm9jBLzZsJUAbUcaToE VMXBwSagLfFniyhIg4iAvcTm2QfAGpgFNjNKPNy0FGyfsECwxK7jvWA2i4CqxM5jO5hAbF4B a4l1R35AbZOXWL3hADOIzSlgI9E/7zs7yCAJgQNsEiemv2WGKHKR6Du9hg3CFpZ4dXwLO4Qt JfH53V42kIMkBKolPu6HKu9glHjx3RbCNpa4uX4DK0gJs4CmxPpd+hBhRYmdv+eCncAswCfx 7msPK8QUXomONiGIElWJvkuHmSBsaYmu9g/sECUeEpvv1ENCpJ9Ron3TPKYJjHKzEBYsYGRc xSiWWlCcm55abFhgihxjmxjBSU/LcgfjsXM+hxgFOBiVeHgV1C/HCrEmlhVX5h5ilOBgVhLh 9Xt0IVaINyWxsiq1KD++qDQntfgQoykwHCcyS4km5wMTcl5JvKGpkbGxsYWJmbmZqbGSOC/H j4uxQgLpiSWp2ampBalFMH1MHJxSDYzS6x9pbf60KUrkjGBt+5Qb7xY/C/qmcEz5r8K+pOgm NdbKuZLzf6g2mMp8XqL6Ootx1cZcN88zmoanN4VfsOgp5w7eez+6vXajbvmG3/E+v72nTjh+ VTyeuWBGw4PG/l0Wf1ffO/f+ximXjTJ+B33ndoVIOysEcKy4oei4+m5Nz79Zxy4FV85WYinO SDTUYi4qTgQA2XNLnJADAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrBLMWRmVeSWpSXmKPExsWy7bCSnK7VtMuxBk9+yFk0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD2KyyYlNSezLLVI3y6BK2Pf83VsBZsPMFYs+NfC3MB4 cj5jFyMnh4SAicT29RtYuhi5OIQEdjNKvPx0gg0iIS1x7MQZ5i5GDiBbWOLw4WKImg+MEpdW nGACibMJaEv82SIKUi4i4CjRu+sw2BxmkDlbpv8CWyAsECixZc9kFhCbRUBVYuexHUwgNq+A tcS6Iz+gjpCXWL3hADOIzSlgI9E/7zs7iC0EVLN50RLWCYx8CxgZVjFKphYU56bnFhsWGOWl lusVJ+YWl+al6yXn525iBIenltYOxhMn4g8xCnAwKvHwnlC5HCvEmlhWXJl7iFGCg1lJhNfv 0YVYId6UxMqq1KL8+KLSnNTiQ4zSHCxK4rzy+ccihQTSE0tSs1NTC1KLYLJMHJxSDYwTr8md upWw4GdR1V7xqZuPfWmSX3RWQu3C37W6fsvEXrMqvV3EPi1r8wuJ8z9YLdRn/A+SytJm+PWm +7j51ekvD6fWTEiMmhAw20yucrG2xX2tlDmCSR3lHSGLzFNbrRfk3Dgry3x+rXzw+3iB9FmG XB56XbZFJVauj6S61/gbPtD8zyi9wl6JpTgj0VCLuag4EQAm6ytvSwIAAA== X-CMS-MailID: 20191119071402epcas1p138f426d591ee81b65f45d092bcad0ebc X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119071402epcas1p138f426d591ee81b65f45d092bcad0ebc References: <20191119071107.1947-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 07:10:56 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11250993 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 4A0BE6C1 for ; Tue, 19 Nov 2019 07:15:01 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 0C834222DF for ; Tue, 19 Nov 2019 07:15:01 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="cX+/LwTE" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727415AbfKSHOJ (ORCPT ); Tue, 19 Nov 2019 02:14:09 -0500 Received: from mailout1.samsung.com ([203.254.224.24]:41557 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726948AbfKSHOI (ORCPT ); Tue, 19 Nov 2019 02:14:08 -0500 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20191119071405epoutp015210d6ac1b2eaac5e5a6112e26fb368a~YfvFkVGBR1797917979epoutp01O for ; Tue, 19 Nov 2019 07:14:05 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20191119071405epoutp015210d6ac1b2eaac5e5a6112e26fb368a~YfvFkVGBR1797917979epoutp01O DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574147645; bh=rrq5p4ujD40dPKhBUhL+cFWt5VPgp+gEKHvQdcP5ll8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=cX+/LwTECLuslAhdX7Dl7u4guBhDLwSm/gaYXo17F4PcC5VaO9BtKJ0HQxGEJme15 tKYbOmwLxnnvRy9zWiE7busrrEbsof1CukbxH5TwAgROblxCQcnI4bNxL04+ccAET0 oT/djZJk7YhWslyUE+zdVOA9ykKBDYTm2CmombN8= Received: from epsnrtp2.localdomain (unknown [182.195.42.163]) by epcas1p2.samsung.com (KnoxPortal) with ESMTP id 20191119071404epcas1p2c8022454f02d62dcc53f8102f07e4dbc~YfvFPt_Zn1338013380epcas1p2p; Tue, 19 Nov 2019 07:14:04 +0000 (GMT) Received: from epsmges1p2.samsung.com (unknown [182.195.40.159]) by epsnrtp2.localdomain (Postfix) with ESMTP id 47HHB340nWzMqYkr; Tue, 19 Nov 2019 07:14:03 +0000 (GMT) Received: from epcas1p1.samsung.com ( [182.195.41.45]) by epsmges1p2.samsung.com (Symantec Messaging Gateway) with SMTP id 99.2C.04235.B3693DD5; Tue, 19 Nov 2019 16:14:03 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p3.samsung.com (KnoxPortal) with ESMTPA id 20191119071403epcas1p3f3d69faad57984fa3d079cf18f0a46dc~YfvDy0wxi2409424094epcas1p3u; Tue, 19 Nov 2019 07:14:03 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119071403epsmtrp1ea19d93955c595f8808f1ed1558f2097~YfvDyGBsh3109131091epsmtrp1_; Tue, 19 Nov 2019 07:14:03 +0000 (GMT) X-AuditID: b6c32a36-25fdf9c00000108b-73-5dd3963b00f9 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id 5A.84.03814.A3693DD5; Tue, 19 Nov 2019 16:14:03 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191119071402epsmtip1aec3c3711649bcbad8e7d460eafd9f7f~YfvDpB8MT1281112811epsmtip1Y; Tue, 19 Nov 2019 07:14:02 +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, Namjae Jeon Subject: [PATCH v2 02/13] exfat: add super block operations Date: Tue, 19 Nov 2019 02:10:56 -0500 Message-Id: <20191119071107.1947-3-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119071107.1947-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrDKsWRmVeSWpSXmKPExsWy7bCmrq71tMuxBpseyFk0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD0qxyYjNTEltUghNS85PyUzL91WyTs43jne1MzAUNfQ 0sJcSSEvMTfVVsnFJ0DXLTMH6DIlhbLEnFKgUEBicbGSvp1NUX5pSapCRn5xia1SakFKToGh QYFecWJucWleul5yfq6VoYGBkSlQZUJOxrKHcgUz5jNW/NrcytrAuLKZsYuRk0NCwETi3r/3 bF2MXBxCAjsYJdY0T2OEcD4xSky4chjK+cYo0bjzNwtMy8vpx8BsIYG9jBK3FinAdXzddo+5 i5GDg01AW+LPFlGQGhEBe4nNsw+wgNQwC2xmlHi4aSlYs7CAtcTVjevYQGwWAVWJS6fvMIPY vEDxl1tOsUEsk5dYveEAWJxTwEaif953dpBBEgI72CRurznKDlHkItG/8R6ULSzx6vgWKFtK 4mV/GzvIQRIC1RIf9zNDhDsYJV58t4WwjSVurt/AClLCLKApsX6XPkRYUWLn77ngIGIW4JN4 97WHFWIKr0RHmxBEiapE36XDTBC2tERX+weoRR4SWxa4Q0Kkn1Hi8blFbBMY5WYhLFjAyLiK USy1oDg3PbXYsMAIOcI2MYJTnpbZDsZF53wOMQpwMCrx8J5QuRwrxJpYVlyZe4hRgoNZSYTX 79GFWCHelMTKqtSi/Pii0pzU4kOMpsBwnMgsJZqcD0zHeSXxhqZGxsbGFiZm5mamxkrivBw/ LsYKCaQnlqRmp6YWpBbB9DFxcEo1MC4IrxAoUI3K0l5RlbD19ueK3Tnnlzdz74vOOnuUbW/h l2O30zZm2s3u6KyLWHxt49Vu7ab8yz2e7x2Ob7IVjLRZyX370sei+5xKQU8+vzh/bi0Ls8Ga Z0xLH2+JWKtT92aCwoH8ms1PT3KErPxr97YwIk/wTvr9j7N0I4NXXTj/NFBRahfvlGQlluKM REMt5qLiRACDn1e2jwMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrOLMWRmVeSWpSXmKPExsWy7bCSnK71tMuxBp/PMVk0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD2KyyYlNSezLLVI3y6BK2PZQ7mCGfMZK35tbmVtYFzZ zNjFyMkhIWAi8XL6MZYuRi4OIYHdjBJ/5/SxQSSkJY6dOMPcxcgBZAtLHD5cDFHzgVFi9vU2 VpA4m4C2xJ8toiDlIgKOEr27DoPNYQaZs2X6L7AFwgLWElc3rgObySKgKnHp9B1mEJsXKP5y yymoXfISqzccAItzCthI9M/7zg5iCwHVbF60hHUCI98CRoZVjJKpBcW56bnFhgVGeanlesWJ ucWleel6yfm5mxjBwamltYPxxIn4Q4wCHIxKPLwnVC7HCrEmlhVX5h5ilOBgVhLh9Xt0IVaI NyWxsiq1KD++qDQntfgQozQHi5I4r3z+sUghgfTEktTs1NSC1CKYLBMHp1QDo+tkzg+3/uQY dLN3ii3L2LXly9y6+0t17nFdtHsydcPr2yXiPwNtnT9x/nNm2zdhvbRa0vNVRvfMS2/NuNHd peHg1L84UTKzPjPxrUxgxPabSluDnqy981sm/Oi+LeKuh/TrSp9HL4p0dcibn3pEZlr2pVZT CTGtdCWRgp06IuXzUq6+eB7ppMRSnJFoqMVcVJwIALS6wxxKAgAA X-CMS-MailID: 20191119071403epcas1p3f3d69faad57984fa3d079cf18f0a46dc X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119071403epcas1p3f3d69faad57984fa3d079cf18f0a46dc References: <20191119071107.1947-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, true); + 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, true); + 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 07:10:57 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11250987 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 B708F138C for ; Tue, 19 Nov 2019 07:14:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 65C03222DF for ; Tue, 19 Nov 2019 07:14:46 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="IMmwrRPJ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727582AbfKSHOp (ORCPT ); Tue, 19 Nov 2019 02:14:45 -0500 Received: from mailout3.samsung.com ([203.254.224.33]:49593 "EHLO mailout3.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727146AbfKSHOL (ORCPT ); Tue, 19 Nov 2019 02:14:11 -0500 Received: from epcas1p3.samsung.com (unknown [182.195.41.47]) by mailout3.samsung.com (KnoxPortal) with ESMTP id 20191119071405epoutp033ff10cfc8849a3552234cb60c22ebfff~YfvF7hCiG2859728597epoutp03v for ; Tue, 19 Nov 2019 07:14:05 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout3.samsung.com 20191119071405epoutp033ff10cfc8849a3552234cb60c22ebfff~YfvF7hCiG2859728597epoutp03v DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574147645; bh=jRWJVgZ+/q6pMFVbl44kxkarbxPHXt0FS3gZATmXIew=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=IMmwrRPJ7OCkgE+eIBRo75ENMZRg4+zMbEEt+qWny7AuNkF3OpBUnoVndUYz7nay0 wTqy1qqEtR7anntK1NJrI8LJWHALsTZkAtnGpLWdGeh9VEHto2r2NHIvV/iZQMJyZ8 8yqI5h5M96r5YiX3OEExlSStOKmhToBDZjtJ/THY= Received: from epsnrtp4.localdomain (unknown [182.195.42.165]) by epcas1p4.samsung.com (KnoxPortal) with ESMTP id 20191119071405epcas1p42e2da69f9b9b5f446705514af16d381d~YfvFoMcZX0776007760epcas1p4g; Tue, 19 Nov 2019 07:14:05 +0000 (GMT) Received: from epsmges1p2.samsung.com (unknown [182.195.40.166]) by epsnrtp4.localdomain (Postfix) with ESMTP id 47HHB36sTdzMqYkl; Tue, 19 Nov 2019 07:14:03 +0000 (GMT) Received: from epcas1p4.samsung.com ( [182.195.41.48]) by epsmges1p2.samsung.com (Symantec Messaging Gateway) with SMTP id BA.2C.04235.B3693DD5; Tue, 19 Nov 2019 16:14:03 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p2.samsung.com (KnoxPortal) with ESMTPA id 20191119071403epcas1p2e2a6d2fca608587547027e46d2185cb9~YfvEQqvCi1338513385epcas1p2-; Tue, 19 Nov 2019 07:14:03 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119071403epsmtrp1842cab9b5e60af64dd1e15c0abf9508f~YfvEP6ron3109231092epsmtrp15; Tue, 19 Nov 2019 07:14:03 +0000 (GMT) X-AuditID: b6c32a36-e07ff7000000108b-79-5dd3963bc449 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id FA.84.03814.B3693DD5; Tue, 19 Nov 2019 16:14:03 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191119071403epsmtip1dc4654b52ba0a1285daa1374c8434b4d~YfvEFKN_A1064910649epsmtip1h; Tue, 19 Nov 2019 07:14:03 +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, Namjae Jeon Subject: [PATCH v2 03/13] exfat: add inode operations Date: Tue, 19 Nov 2019 02:10:57 -0500 Message-Id: <20191119071107.1947-4-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119071107.1947-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrPKsWRmVeSWpSXmKPExsWy7bCmga71tMuxBh+em1s0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD0qxyYjNTEltUghNS85PyUzL91WyTs43jne1MzAUNfQ 0sJcSSEvMTfVVsnFJ0DXLTMH6DIlhbLEnFKgUEBicbGSvp1NUX5pSapCRn5xia1SakFKToGh QYFecWJucWleul5yfq6VoYGBkSlQZUJORu/G6cwFt+4zV7yedomlgXFvI3MXIyeHhICJxLnm 1UA2F4eQwA5GiYauO6wQzidGia57r9ghnG+MEiu/rWaDafnz8DhU1V5GiRkblzHCtbTv+AKU 4eBgE9CW+LNFFKRBRMBeYvPsAywgNcwCmxklHm5aygKSEBYwlXh38QEjiM0ioCpxbMZfdhCb V8Ba4s+1c+wQ2+QlVm84AHYsp4CNRP+872AnSQjsYZNo6dsPVeQi8XLRNKiPhCVeHd8CFZeS eNnfxg5ykIRAtcTH/VAlHYwSL77bQtjGEjfXbwC7mVlAU2L9Ln2IsKLEzt9zwU5jFuCTePe1 hxViCq9ER5sQRImqRN+lw0wQtrREV/sHqKUeEv9OH4QGXD+jxN2dZ1gmMMrNQtiwgJFxFaNY akFxbnpqsWGBEXKcbWIEJz4tsx2Mi875HGIU4GBU4uE9oXI5Vog1say4MvcQowQHs5IIr9+j C7FCvCmJlVWpRfnxRaU5qcWHGE2BATmRWUo0OR+YlPNK4g1NjYyNjS1MzMzNTI2VxHk5flyM FRJITyxJzU5NLUgtgulj4uCUamC0fTCl817APIZNa3b9PrfypV3XzhNO3j3zj7cKntYPWOCT tMB31tqJYQbXDnv8bJAQu109LWS/+srPVz65O9T9M65LdS7tllGN+PXi56SK2I+nzfff47B5 OzNg1bcjrp8j83vXPTv71EMy3a1lm9rXt2a12zKXys/Vca+KLGKe3rB4rcCNWZ7MSizFGYmG WsxFxYkAaNrUvpIDAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrBLMWRmVeSWpSXmKPExsWy7bCSnK71tMuxBp2NihbNi9ezWaxcfZTJ 4vrdW8wWe/aeZLG4vGsOm8X/Wc9ZLX5Mr7fY8u8Iq8Wl9x9YHDg9ds66y+6xf+4ado/dNxvY PPq2rGL0+LxJzuPQ9jdsHrefbWMJYI/isklJzcksSy3St0vgyujdOJ254NZ95orX0y6xNDDu bWTuYuTkkBAwkfjz8DhrFyMXh5DAbkaJ8w+XskIkpCWOnTgDVMQBZAtLHD5cDFHzgVFi9YUW FpA4m4C2xJ8toiDlIgKOEr27DrOA1DCDzNky/RcjSEJYwFTi3cUHYDaLgKrEsRl/2UFsXgFr iT/XzrFD7JKXWL3hANhBnAI2Ev3zvoPFhYBqNi9awjqBkW8BI8MqRsnUguLc9NxiwwKjvNRy veLE3OLSvHS95PzcTYzg8NTS2sF44kT8IUYBDkYlHt4TKpdjhVgTy4orcw8xSnAwK4nw+j26 ECvEm5JYWZValB9fVJqTWnyIUZqDRUmcVz7/WKSQQHpiSWp2ampBahFMlomDU6qBMUXv9hYV 7nnfk+8eUpXk11rlcjdqz/m72QFNS25wWJW4rFuZ0XDvq/oR2SLt9ktR8dqSMhINsRfUZi7p iWO8KvjrV1fKjDVVf3yf3GV783j3g3nJz3Y3za0/bc92fnqoVeoT3ilN/fd6g/ff5WqOV65V Ot7RmVw5/Q73jXYxzRvTnrp0TNL8pcRSnJFoqMVcVJwIAGLMjS5LAgAA X-CMS-MailID: 20191119071403epcas1p2e2a6d2fca608587547027e46d2185cb9 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119071403epcas1p2e2a6d2fca608587547027e46d2185cb9 References: <20191119071107.1947-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 07:10:58 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11250983 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 0E3AF6C1 for ; Tue, 19 Nov 2019 07:14:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id C40FB222DF for ; Tue, 19 Nov 2019 07:14:44 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="U5Z7sDeU" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727573AbfKSHOM (ORCPT ); Tue, 19 Nov 2019 02:14:12 -0500 Received: from mailout3.samsung.com ([203.254.224.33]:49607 "EHLO mailout3.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727217AbfKSHOK (ORCPT ); Tue, 19 Nov 2019 02:14:10 -0500 Received: from epcas1p2.samsung.com (unknown [182.195.41.46]) by mailout3.samsung.com (KnoxPortal) with ESMTP id 20191119071406epoutp03441dc357c96f9d14234125ea5f9c8dc7~YfvGz6sy82844528445epoutp037 for ; Tue, 19 Nov 2019 07:14:06 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout3.samsung.com 20191119071406epoutp03441dc357c96f9d14234125ea5f9c8dc7~YfvGz6sy82844528445epoutp037 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574147646; bh=smI7kwqIbE9lWeY5Ol2C6KxaA85uIMSUmsXYwlTv1Tg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=U5Z7sDeUxZUNU8oNsUzHxJWp8cViIgj7Wphfgg4JFcLW3TOc2Pr2ag1PiSh1C0U3Y O0z8BX9flPY7ZtsCQKUNppMqqODsrDM57xkDJj+CUMJjEN7VL8TSzKQJiR3lKQfQlI dTfukVfkL83OkL5VN1lO0vxWNAmVTfuM/v/jvAII= Received: from epsnrtp2.localdomain (unknown [182.195.42.163]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20191119071405epcas1p145b92a18344cc89131b07012ae949c3c~YfvGU32TB0130401304epcas1p1u; Tue, 19 Nov 2019 07:14:05 +0000 (GMT) Received: from epsmges1p5.samsung.com (unknown [182.195.40.166]) by epsnrtp2.localdomain (Postfix) with ESMTP id 47HHB459BNzMqYkj; Tue, 19 Nov 2019 07:14:04 +0000 (GMT) Received: from epcas1p2.samsung.com ( [182.195.41.46]) by epsmges1p5.samsung.com (Symantec Messaging Gateway) with SMTP id C3.E8.04237.C3693DD5; Tue, 19 Nov 2019 16:14:04 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p4.samsung.com (KnoxPortal) with ESMTPA id 20191119071404epcas1p4a26ef051aecc9386f90111eddaa4e8f5~YfvExlNy-0776107761epcas1p4d; Tue, 19 Nov 2019 07:14:04 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119071404epsmtrp1ae887a8420d75db3acfe1e9b8aa2dda8~YfvEwvmrT3109131091epsmtrp1-; Tue, 19 Nov 2019 07:14:04 +0000 (GMT) X-AuditID: b6c32a39-913ff7000000108d-13-5dd3963c06b7 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 98.14.03654.B3693DD5; Tue, 19 Nov 2019 16:14:03 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191119071403epsmtip165dcbed85e011b023a88c13dd946bf0a~YfvEggfLS1064910649epsmtip1i; Tue, 19 Nov 2019 07:14:03 +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, Namjae Jeon Subject: [PATCH v2 04/13] exfat: add directory operations Date: Tue, 19 Nov 2019 02:10:58 -0500 Message-Id: <20191119071107.1947-5-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119071107.1947-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrNKsWRmVeSWpSXmKPExsWy7bCmnq7NtMuxBq+fq1o0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD0qxyYjNTEltUghNS85PyUzL91WyTs43jne1MzAUNfQ 0sJcSSEvMTfVVsnFJ0DXLTMH6DIlhbLEnFKgUEBicbGSvp1NUX5pSapCRn5xia1SakFKToGh QYFecWJucWleul5yfq6VoYGBkSlQZUJORnNXK0vB9S6mij1TO9kbGD+dZ+xi5OCQEDCR6D9f 3sXIySEksINR4uQ85i5GLiD7E6PElY1XGSGcb4wSk56+YAepAmnYtLSRFaJjL6PElnuCcB3b v11jA5nKJqAt8WeLKEiNiIC9xObZB1hAapgFNjNKPNy0lAUkISxgKXFv/nUwm0VAVWJf72Im EJtXwFpiYd89Fohl8hKrNxxgBrE5BWwk+ud9ZwcZJCGwh01i25Q9bBBFLhL9O35CXScs8er4 FihbSuJlfxs7xJvVEh/3M0OEOxglXny3hbCNJW6u38AKUsIsoCmxfpc+RFhRYufvuYwgNrMA n8S7rz2sEFN4JTrahCBKVCX6Lh1mgrClJbraP0At9ZDYdGktOyR4+hkluntcJzDKzUJYsICR cRWjWGpBcW56arFhgSlyfG1iBCc8LcsdjMfO+RxiFOBgVOLhVVC/HCvEmlhWXJl7iFGCg1lJ hNfv0YVYId6UxMqq1KL8+KLSnNTiQ4ymwHCcyCwlmpwPTMZ5JfGGpkbGxsYWJmbmZqbGSuK8 HD8uxgoJpCeWpGanphakFsH0MXFwSjUwemyc2+vaZ5xiYiyzLW/bS841Gqf3X82LVNwbEHHj 8ir2gzpbeRbqy5Svu+i6IyEz3G2puOT6mqlT5jX6y4Umleoe/dqTd17KY/2usitGm/4V5pt4 JX7qXdb/c5LwTWUWzhXTl9Qqa9w49+js7q8Hkm69Sw7UPfsu5/2Nt5Yi1pxBXgkeJ9+rKbEU ZyQaajEXFScCALrkEn+OAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrBLMWRmVeSWpSXmKPExsWy7bCSnK71tMuxBpv22Vg0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD2KyyYlNSezLLVI3y6BK6O5q5Wl4HoXU8WeqZ3sDYyf zjN2MXJySAiYSGxa2sjaxcjFISSwm1Fi3bfnLBAJaYljJ84wdzFyANnCEocPF0PUfGCUuDt5 JztInE1AW+LPFlGQchEBR4neXYdZQGqYQeZsmf4LbIGwgKXEvfnXwWayCKhK7OtdzARi8wpY Syzsuwe1S15i9YYDzCA2p4CNRP+87+wgthBQzeZFS1gnMPItYGRYxSiZWlCcm55bbFhgmJda rlecmFtcmpeul5yfu4kRHJ5amjsYLy+JP8QowMGoxMN7QuVyrBBrYllxZe4hRgkOZiURXr9H F2KFeFMSK6tSi/Lji0pzUosPMUpzsCiJ8z7NOxYpJJCeWJKanZpakFoEk2Xi4JRqYJzisuvk HaPyuVvjm8zu7HPa6twyz3PF+bQHDy8EWDv0+9bbF6/MqJvQni9dtubXncaJ98yX8uYs+63x Rcu08aBp7h8TniMSLrNUQ3zFldr/c/cdkt1yLE679+OWj2UT1L9KibhMdPfxe7O7Q1Tlxf7j Hhq1P4NyJ65uFO0+N7P/ceYp/f74bCWW4oxEQy3mouJEAPKcwfFLAgAA X-CMS-MailID: 20191119071404epcas1p4a26ef051aecc9386f90111eddaa4e8f5 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119071404epcas1p4a26ef051aecc9386f90111eddaa4e8f5 References: <20191119071107.1947-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 07:10:59 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11250985 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 C5E0F138C for ; Tue, 19 Nov 2019 07:14:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 9D29022317 for ; Tue, 19 Nov 2019 07:14:45 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="V1vCFYY9" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727530AbfKSHOL (ORCPT ); Tue, 19 Nov 2019 02:14:11 -0500 Received: from mailout1.samsung.com ([203.254.224.24]:41578 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727205AbfKSHOK (ORCPT ); Tue, 19 Nov 2019 02:14:10 -0500 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20191119071406epoutp01f5fa081128baccbc3d392fbe3cc6c8e3~YfvGsCndi1802418024epoutp01H for ; Tue, 19 Nov 2019 07:14:06 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20191119071406epoutp01f5fa081128baccbc3d392fbe3cc6c8e3~YfvGsCndi1802418024epoutp01H DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574147646; bh=UynEUjd2J6AJBENA5flggnmE+/uNET28DrdikmIOav4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=V1vCFYY94cHt9jqqcMjtsI1J/DykWb5TkouRWBStOt1+9bil3CcTsysUlO4Q51XXR Wv88Gq9ndOhEiyMCQvIt/p1HpUnHcw60Ti9cWjMQwsNAzvh4UifUFcDS15ltX9may8 PnYvNHJzj0XPBlEPblG81gRHhSn1WvupZCWfEyRs= Received: from epsnrtp1.localdomain (unknown [182.195.42.162]) by epcas1p4.samsung.com (KnoxPortal) with ESMTP id 20191119071405epcas1p434f8cc76020feb71c46eaed930f48990~YfvGYoeia0493704937epcas1p4M; Tue, 19 Nov 2019 07:14:05 +0000 (GMT) Received: from epsmges1p4.samsung.com (unknown [182.195.40.165]) by epsnrtp1.localdomain (Postfix) with ESMTP id 47HHB46XVSzMqYlh; Tue, 19 Nov 2019 07:14:04 +0000 (GMT) Received: from epcas1p1.samsung.com ( [182.195.41.45]) by epsmges1p4.samsung.com (Symantec Messaging Gateway) with SMTP id D1.7D.04406.C3693DD5; Tue, 19 Nov 2019 16:14:04 +0900 (KST) Received: from epsmtrp2.samsung.com (unknown [182.195.40.14]) by epcas1p4.samsung.com (KnoxPortal) with ESMTPA id 20191119071404epcas1p4f8df45690c07c4dd032af9cbfb5efcc6~YfvFJip3O0222302223epcas1p4a; Tue, 19 Nov 2019 07:14:04 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp2.samsung.com (KnoxPortal) with ESMTP id 20191119071404epsmtrp2b594ed25b8fea1e72f6c38494b3e975c~YfvFIzWJD0193901939epsmtrp2L; Tue, 19 Nov 2019 07:14:04 +0000 (GMT) X-AuditID: b6c32a38-95fff70000001136-e4-5dd3963c3feb Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 49.14.03654.C3693DD5; Tue, 19 Nov 2019 16:14:04 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191119071404epsmtip1429b751e4b34d8faa6c3577b942922de~YfvE_XEYb1281112811epsmtip1a; Tue, 19 Nov 2019 07:14:04 +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, Namjae Jeon Subject: [PATCH v2 05/13] exfat: add file operations Date: Tue, 19 Nov 2019 02:10:59 -0500 Message-Id: <20191119071107.1947-6-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119071107.1947-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrNKsWRmVeSWpSXmKPExsWy7bCmrq7NtMuxBgfWGVo0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD0qxyYjNTEltUghNS85PyUzL91WyTs43jne1MzAUNfQ 0sJcSSEvMTfVVsnFJ0DXLTMH6DIlhbLEnFKgUEBicbGSvp1NUX5pSapCRn5xia1SakFKToGh QYFecWJucWleul5yfq6VoYGBkSlQZUJOxuM7JxgLfvhUvNr9kK2B8ZhtFyMnh4SAicS7p50s XYxcHEICOxgl3hz6wQjhfGKU6OlsZ4ZwvjFKdB/Zww7T8nHhKjYQW0hgL6PEk14mCBuoY2ur fhcjBwebgLbEny2iIGERAXuJzbMPgG1gFtjMKPFw01IWkIQw0Jz2CV1gc1gEVCUWLp7FCGLz ClhLvNnTwAKxS15i9YYDzCA2p4CNRP+87+wggyQEDrBJPG2aB1XkItE87yczhC0s8er4FqhD pSQ+v9vLBnKQhEC1xMf9UCUdjBIvvkO9byxxc/0GVpASZgFNifW79CHCihI7f88FO4dZgE/i 3dceVogpvBIdbUIQJaoSfZcOM0HY0hJd7R+glnpILJs3lw0SbP2MEl1r/7BOYJSbhbBhASPj Kkax1ILi3PTUYsMCE+T42sQITnhaFjsY95zzOcQowMGoxMN7QuVyrBBrYllxZe4hRgkOZiUR Xr9HF2KFeFMSK6tSi/Lji0pzUosPMZoCA3Iis5Rocj4wGeeVxBuaGhkbG1uYmJmbmRorifNy /LgYKySQnliSmp2aWpBaBNPHxMEp1cBYl8vc9HbDwxM5yfM/Hd2cc2pW7cSUNPG8T+GGz3oW /V+cGpgt2iJ11fDGjby/+51uGP86I/VOde+7w7XGeedmP3OxOWHfFR9/0GrbqS8GElNnS8sZ 1Z+WnTBHe/IuDRUPqWO7/vnPcDEoWDQh+G9n1gK97LrS7R/fyFaz/P0WaJX5YuHdWWLrlViK MxINtZiLihMBdomq744DAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrGLMWRmVeSWpSXmKPExsWy7bCSnK7NtMuxBgvPylg0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD2KyyYlNSezLLVI3y6BK+PxnROMBT98Kl7tfsjWwHjM touRk0NCwETi48JVbF2MXBxCArsZJdouHWOCSEhLHDtxhrmLkQPIFpY4fLgYouYDo8SdKWfY QOJsAtoSf7aIgpSLCDhK9O46zAJSwwwyZ8v0X4wgCWGgBe0TuthAbBYBVYmFi2eBxXkFrCXe 7GlggdglL7F6wwFmEJtTwEaif953dhBbCKhm86IlrBMY+RYwMqxilEwtKM5Nzy02LDDMSy3X K07MLS7NS9dLzs/dxAgOTS3NHYyXl8QfYhTgYFTi4T2hcjlWiDWxrLgy9xCjBAezkgiv36ML sUK8KYmVValF+fFFpTmpxYcYpTlYlMR5n+YdixQSSE8sSc1OTS1ILYLJMnFwSjUwsk1K68la 8tDj//wwgWMMfVGXJeVXPmWfOf/Lw9Untn44z3ThQMLHopmHfq7tl1hyaFHwuvvz2LVfl266 u3aSzYlnLBPfuEpGTZnClqdreyf96g6Frzt4soyUd15XV+H49EG+cuXd8E92+yfP/C/DvlZw jbZoigwnm8Il98Cnlv/FJ0wwfvLIYIMSS3FGoqEWc1FxIgD/gjWmSQIAAA== X-CMS-MailID: 20191119071404epcas1p4f8df45690c07c4dd032af9cbfb5efcc6 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119071404epcas1p4f8df45690c07c4dd032af9cbfb5efcc6 References: <20191119071107.1947-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 07:11:00 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11250981 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 58C3A6C1 for ; Tue, 19 Nov 2019 07:14:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 1BCDA22312 for ; Tue, 19 Nov 2019 07:14:42 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="SAZqnBIh" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727594AbfKSHOM (ORCPT ); Tue, 19 Nov 2019 02:14:12 -0500 Received: from mailout2.samsung.com ([203.254.224.25]:62576 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727351AbfKSHOK (ORCPT ); Tue, 19 Nov 2019 02:14:10 -0500 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20191119071407epoutp02c23649ca9ae5b7e19483431cb6688394~YfvIW8RfB1270812708epoutp02J for ; Tue, 19 Nov 2019 07:14:07 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20191119071407epoutp02c23649ca9ae5b7e19483431cb6688394~YfvIW8RfB1270812708epoutp02J DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574147648; bh=moFSjqom8tMKVgnAIE+bliJo2nyGZ77U3BbjnQGWtEE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SAZqnBIhNh7K3LceeHn8uobPMA+4GMsC4zAgtlnqWrksIgWsViDX/XiDUIWN4CS1P PgmUBVaSnDgnhNLvtNLR17X8nQEySZ5cOnEaDAkVGp/Y8JGrEisML3jtKHktC3NdxS NXer8a2uZEyGsLVHgCLsqzoi+ziEZCqE5flmpU54= Received: from epsnrtp1.localdomain (unknown [182.195.42.162]) by epcas1p2.samsung.com (KnoxPortal) with ESMTP id 20191119071407epcas1p2e1051e841f2846887fb7ec1d583ed056~YfvIAoxfE1345313453epcas1p2D; Tue, 19 Nov 2019 07:14:07 +0000 (GMT) Received: from epsmges1p1.samsung.com (unknown [182.195.40.164]) by epsnrtp1.localdomain (Postfix) with ESMTP id 47HHB64SWjzMqYm1; Tue, 19 Nov 2019 07:14:06 +0000 (GMT) Received: from epcas1p4.samsung.com ( [182.195.41.48]) by epsmges1p1.samsung.com (Symantec Messaging Gateway) with SMTP id 7F.6B.04072.D3693DD5; Tue, 19 Nov 2019 16:14:05 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p2.samsung.com (KnoxPortal) with ESMTPA id 20191119071405epcas1p29e1af8242cce221c45eb529921028e48~YfvFyNF8c1345313453epcas1p28; Tue, 19 Nov 2019 07:14:05 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119071405epsmtrp15d7e934aede522fa7359bee17b85e6fc~YfvFu3eGc3109231092epsmtrp17; Tue, 19 Nov 2019 07:14:05 +0000 (GMT) X-AuditID: b6c32a35-9bdff70000000fe8-a2-5dd3963ddb5d Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id AB.84.03814.C3693DD5; Tue, 19 Nov 2019 16:14:05 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191119071404epsmtip121d635b38fbd09398eaf56d236c30020~YfvFfhz3g1281112811epsmtip1b; Tue, 19 Nov 2019 07:14:04 +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, Namjae Jeon Subject: [PATCH v2 06/13] exfat: add exfat entry operations Date: Tue, 19 Nov 2019 02:11:00 -0500 Message-Id: <20191119071107.1947-7-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119071107.1947-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrFKsWRmVeSWpSXmKPExsWy7bCmga7dtMuxBseOqFg0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD0qxyYjNTEltUghNS85PyUzL91WyTs43jne1MzAUNfQ 0sJcSSEvMTfVVsnFJ0DXLTMH6DIlhbLEnFKgUEBicbGSvp1NUX5pSapCRn5xia1SakFKToGh QYFecWJucWleul5yfq6VoYGBkSlQZUJOxq0lpgVNURXH/55hamDscOti5OCQEDCROLvWtIuR i0NIYAejxOrvWxghnE+MEvuXH2GHcL4xSvSu/AiU4QTruHj6ClRiL6PExH3PEFp2NTYwgcxl E9CW+LNFFKRBRMBeYvPsAywgNcwCmxklHm5aygKSEBawltj17hjYVBYBVYnn26eD2bxA8aUX v7BDbJOXWL3hADOIzSlgI9E/7ztUfAebxLEWCQjbRWLT1lMsELawxKvjW6BqpCRe9rexQ/xZ LfFxPzNEuINR4sV3WwjbWOLm+g2sICXMApoS63fpQ4QVJXb+ngt2DbMAn8S7rz2sEFN4JTra hCBKVCX6Lh1mgrClJbraP0At9ZA4OXs6GyRE+hkl5j7uYZ/AKDcLYcMCRsZVjGKpBcW56anF hgWGyNG1iRGc7rRMdzBOOedziFGAg1GJh1dB/XKsEGtiWXFl7iFGCQ5mJRFev0cXYoV4UxIr q1KL8uOLSnNSiw8xmgLDcSKzlGhyPjAV55XEG5oaGRsbW5iYmZuZGiuJ83L8uBgrJJCeWJKa nZpakFoE08fEwSnVwCgoeHphVv8TNh3Gf737jPbaPLhiobgySqdu3m7ht8lNu40uN7EL6F5m 9Jv6WdTrjPXcwrnb3gbuf8tjfv5Nfa6AhVqESNMd1bV9EmdCZrvv5d4jsXmO0BGF9nt1POxN AUdvLX9z4s5GsYfKcvkrlnEYOYq6Oe/KaBObHMeRs/bencnOS545zFZiKc5INNRiLipOBABu o3XdjQMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrKLMWRmVeSWpSXmKPExsWy7bCSnK7ttMuxBtefs1g0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD2KyyYlNSezLLVI3y6BK+PWEtOCpqiK43/PMDUwdrh1 MXJySAiYSFw8fYW9i5GLQ0hgN6PEpC8PWSAS0hLHTpxh7mLkALKFJQ4fLoao+cAocfrUZyaQ OJuAtsSfLaIg5SICjhK9uw6zgNQwg8zZMv0XI0hCWMBaYte7Y2A2i4CqxPPt08FsXqD40otf 2CF2yUus3nCAGcTmFLCR6J/3HSwuBFSzedES1gmMfAsYGVYxSqYWFOem5xYbFhjlpZbrFSfm Fpfmpesl5+duYgQHppbWDsYTJ+IPMQpwMCrx8J5QuRwrxJpYVlyZe4hRgoNZSYTX79GFWCHe lMTKqtSi/Pii0pzU4kOM0hwsSuK88vnHIoUE0hNLUrNTUwtSi2CyTBycUg2MbaeXSLCtvfns 0qzrD5JjfAVME1Z+uZ799kd51hRRHZHqu+sY3xeGbV+54rNB3YXT395f3F4n13dRWkhq38yi 3W5xl7ymmTVdK9D5klV8hV/UykjsiV2PEouETddTz/1PvgosYv3k1c8eLmz2KT1IYmqHe6OX 59aD0W4HHda8sEutKdz1UM5BiaU4I9FQi7moOBEAne5BCUgCAAA= X-CMS-MailID: 20191119071405epcas1p29e1af8242cce221c45eb529921028e48 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119071405epcas1p29e1af8242cce221c45eb529921028e48 References: <20191119071107.1947-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 07:11:01 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11250971 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 59938184E for ; Tue, 19 Nov 2019 07:14:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 309A222309 for ; Tue, 19 Nov 2019 07:14:11 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="Yqv/Di3+" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727506AbfKSHOK (ORCPT ); Tue, 19 Nov 2019 02:14:10 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:35503 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725869AbfKSHOK (ORCPT ); Tue, 19 Nov 2019 02:14:10 -0500 Received: from epcas1p3.samsung.com (unknown [182.195.41.47]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20191119071407epoutp0484d3f6e14ad129559fffc4d44419e99b~YfvHoGhD41827918279epoutp04Y for ; Tue, 19 Nov 2019 07:14:07 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20191119071407epoutp0484d3f6e14ad129559fffc4d44419e99b~YfvHoGhD41827918279epoutp04Y DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574147647; bh=iRjw2RD0asFmnqKKqPfk/KOVSDaCUefdsIbM8/AfuQo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Yqv/Di3+V5PpvEqRyG1f6R5fGCJC7gfhvSFGAzLh9sxeJVzl00IsAdP92eLmOI3uC Cv0NHdZcCbmJ+F2nrJFbceDicZQ3RNyLcerCQ1u4T8cz/xubTHmB2vu/iOs6yxPxJy HhfJ32XpHpUhndm4WU75opHiCTY7Cnu1kifHVXS8= Received: from epsnrtp2.localdomain (unknown [182.195.42.163]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20191119071406epcas1p19ff57aecdbdee555dec2eac8ffe7e355~YfvHXfLoC3222032220epcas1p1I; Tue, 19 Nov 2019 07:14:06 +0000 (GMT) Received: from epsmges1p2.samsung.com (unknown [182.195.40.162]) by epsnrtp2.localdomain (Postfix) with ESMTP id 47HHB607TmzMqYkh; Tue, 19 Nov 2019 07:14:06 +0000 (GMT) Received: from epcas1p1.samsung.com ( [182.195.41.45]) by epsmges1p2.samsung.com (Symantec Messaging Gateway) with SMTP id 3D.2C.04235.D3693DD5; Tue, 19 Nov 2019 16:14:05 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p2.samsung.com (KnoxPortal) with ESMTPA id 20191119071405epcas1p2c282cb850ef14d181208554796403739~YfvGLb2JD1561515615epcas1p21; Tue, 19 Nov 2019 07:14:05 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119071405epsmtrp1a9b5ec643e21c4101e0985c9f9d14cf3~YfvGK0Z513109131091epsmtrp1B; Tue, 19 Nov 2019 07:14:05 +0000 (GMT) X-AuditID: b6c32a36-25fdf9c00000108b-86-5dd3963d43e5 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 6B.14.03654.D3693DD5; Tue, 19 Nov 2019 16:14:05 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191119071405epsmtip16b4e69bf7612d06dec0d3d58beca4ec2~YfvGAka601281112811epsmtip1c; Tue, 19 Nov 2019 07:14:05 +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, Namjae Jeon Subject: [PATCH v2 07/13] exfat: add bitmap operations Date: Tue, 19 Nov 2019 02:11:01 -0500 Message-Id: <20191119071107.1947-8-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119071107.1947-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrFKsWRmVeSWpSXmKPExsWy7bCmrq7ttMuxBnNOWls0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD0qxyYjNTEltUghNS85PyUzL91WyTs43jne1MzAUNfQ 0sJcSSEvMTfVVsnFJ0DXLTMH6DIlhbLEnFKgUEBicbGSvp1NUX5pSapCRn5xia1SakFKToGh QYFecWJucWleul5yfq6VoYGBkSlQZUJOxqT3KgXtLhXHV2U0MB4z7mLk5JAQMJGYfuw5G4gt JLCDUWLBLK0uRi4g+xOjxOKFi5ghnG+MEhd/z2aC6Zh29zsbRGIvo8Td9V3scC2n92wBynBw sAloS/zZIgrSICJgL7F59gEWkBpmgc2MEg83LWUBSQgLmElc2H2DFcRmEVCVuLvxEyOIzStg LdF07hczxDZ5idUbDoDZnAI2Ev3zvoMtkxDYwSbR9uEZC0SRi8S3C01Q5wlLvDq+hR3ClpJ4 2d/GDnKQhEC1xMf9UDM7GCVefLeFsI0lbq7fwApSwiygKbF+lz5EWFFi5++5YOcwC/BJvPva wwoxhVeio00IokRVou/SYail0hJd7R+glnpI9O79wgoJkn5GiTt3VzJNYJSbhbBhASPjKkax 1ILi3PTUYsMCI+To2sQITndaZjsYF53zOcQowMGoxMN7QuVyrBBrYllxZe4hRgkOZiURXr9H F2KFeFMSK6tSi/Lji0pzUosPMZoCA3Iis5Rocj4wFeeVxBuaGhkbG1uYmJmbmRorifNy/LgY KySQnliSmp2aWpBaBNPHxMEp1cAoPeHFnd2HHm+9MV/9WPzWoi0ppQf81zwVXPr97MYXZi1d v7Z/e/GUQ853GV9TnVHCGtcIyc+au2Yv7jM6f6CzZG/2v+T9DQ+LT3G3L8yM9f8fybJPxvfo 9mbrn5eqy3uK1nIo7rLQPZ5sd/TuceblJz66socrceYdWhw09cGRgh4N6edmR9ZOV2Ipzkg0 1GIuKk4EAPZmyhSNAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrMLMWRmVeSWpSXmKPExsWy7bCSnK7ttMuxBtvXKls0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD2KyyYlNSezLLVI3y6BK2PSe5WCdpeK46syGhiPGXcx cnJICJhITLv7na2LkYtDSGA3o8SjnbcZIRLSEsdOnGHuYuQAsoUlDh8uBgkLCXxglLi8xREk zCagLfFniyhIWETAUaJ312EWkDHMIGO2TP8FNkZYwEziwu4brCA2i4CqxN2Nn8DivALWEk3n fjFDrJKXWL3hAJjNKWAj0T/vOzvELmuJzYuWsE5g5FvAyLCKUTK1oDg3PbfYsMAwL7Vcrzgx t7g0L10vOT93EyM4KLU0dzBeXhJ/iFGAg1GJh/eEyuVYIdbEsuLK3EOMEhzMSiK8fo8uxArx piRWVqUW5ccXleakFh9ilOZgURLnfZp3LFJIID2xJDU7NbUgtQgmy8TBKdXAaOWW+lA9Uean 4sTlx7T/p0fsdo6cb7Se77fDuk3bfJTWF5/f1Jni5Lhxa1HnjqIdmkVnF36astJg+xfeadvv Gq0Mcb65vNug5tfV0CjXrSHRq46bil0W2sn2VerWg8Ts2KcLsjddKEjY8KL1gubkhQska2W6 dCa3SG36rc7IcmX9/d4E96NSzkosxRmJhlrMRcWJAIWL2wFGAgAA X-CMS-MailID: 20191119071405epcas1p2c282cb850ef14d181208554796403739 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119071405epcas1p2c282cb850ef14d181208554796403739 References: <20191119071107.1947-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 07:11:02 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11250989 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 0B277138C for ; Tue, 19 Nov 2019 07:14:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id C9BC4222DF for ; Tue, 19 Nov 2019 07:14:54 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="shQmnGNi" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727831AbfKSHOr (ORCPT ); Tue, 19 Nov 2019 02:14:47 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:35523 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727354AbfKSHOL (ORCPT ); Tue, 19 Nov 2019 02:14:11 -0500 Received: from epcas1p2.samsung.com (unknown [182.195.41.46]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20191119071408epoutp0442b8a2d7a5e96dcf421db3a5569486c9~YfvIY6YkZ1848718487epoutp04M for ; Tue, 19 Nov 2019 07:14:08 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20191119071408epoutp0442b8a2d7a5e96dcf421db3a5569486c9~YfvIY6YkZ1848718487epoutp04M DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574147648; bh=cMpYo+3xAlzGcb/z0sUvvooDr48c4npcG1qhTtMBMh8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=shQmnGNiHdzMV5R1wFVTOCeygNdl33PMPz8KUwdBNnP1H8EoRRITz4qORCNh63N9q eKDWZ0+tWkjHTYE84Cjd8LQvoAXncN8vc+5kWA8+v0R/266m/gkYKMMsKMk7ZxNywN Zg144TrxBK9qD1BZ7N4HnU0TlWHPXtsWtD3DElBQ= Received: from epsnrtp2.localdomain (unknown [182.195.42.163]) by epcas1p4.samsung.com (KnoxPortal) with ESMTP id 20191119071407epcas1p430f22ba9b297930eacfe7bad467a67cb~YfvH-HOR90775207752epcas1p4h; Tue, 19 Nov 2019 07:14:07 +0000 (GMT) Received: from epsmges1p3.samsung.com (unknown [182.195.40.161]) by epsnrtp2.localdomain (Postfix) with ESMTP id 47HHB6459CzMqYkW; Tue, 19 Nov 2019 07:14:06 +0000 (GMT) Received: from epcas1p2.samsung.com ( [182.195.41.46]) by epsmges1p3.samsung.com (Symantec Messaging Gateway) with SMTP id EF.3F.04080.E3693DD5; Tue, 19 Nov 2019 16:14:06 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p3.samsung.com (KnoxPortal) with ESMTPA id 20191119071406epcas1p34788f32abd876190f2b0a5c2ba39e4b1~YfvGvYVyz2635426354epcas1p3z; Tue, 19 Nov 2019 07:14:06 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119071406epsmtrp1572392cc270817e68b679085b6fa55b1~YfvGuvkJB3109131091epsmtrp1D; Tue, 19 Nov 2019 07:14:06 +0000 (GMT) X-AuditID: b6c32a37-c29d39c000000ff0-43-5dd3963ee751 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 4C.14.03654.E3693DD5; Tue, 19 Nov 2019 16:14:06 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191119071405epsmtip12cec80fb64360259dde18b8fdf31cca3~YfvGfbXOD1281112811epsmtip1d; Tue, 19 Nov 2019 07:14:05 +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, Namjae Jeon Subject: [PATCH v2 08/13] exfat: add exfat cache Date: Tue, 19 Nov 2019 02:11:02 -0500 Message-Id: <20191119071107.1947-9-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119071107.1947-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrDKsWRmVeSWpSXmKPExsWy7bCmnq7dtMuxBhOuKFo0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD0qxyYjNTEltUghNS85PyUzL91WyTs43jne1MzAUNfQ 0sJcSSEvMTfVVsnFJ0DXLTMH6DIlhbLEnFKgUEBicbGSvp1NUX5pSapCRn5xia1SakFKToGh QYFecWJucWleul5yfq6VoYGBkSlQZUJOxoP2j6wFbW4VexeeY2lgXG3RxcjBISFgInHjrE4X IxeHkMAORom9y1azQDifGCWaHs9lg3C+MUrcO3kbyOEE63hyaCo7RGIvo0TblslMcC1nPs5i AZnLJqAt8WeLKEiDiIC9xObZB8DGMgtsZpR4uGkpC0hCWMBA4vPsa2BTWQRUJS4uXcwOYvMK WEusvP2QEWKbvMTqDQeYQWxOARuJ/nnfwTZLCBxgk5jXPJkdoshF4uaWtcwQtrDEq+NboOJS Ei/729ghHq2W+LgfqqSDUeLFd1sI21ji5voNrCAlzAKaEut36UOEFSV2/p4LdgKzAJ/Eu689 rBBTeCU62oQgSlQl+i4dZoKwpSW62j9ALfWQeLZ8MzTg+hkl5s65wzyBUW4WwoYFjIyrGMVS C4pz01OLDQuMkSNsEyM45WmZ72DccM7nEKMAB6MSD6+C+uVYIdbEsuLK3EOMEhzMSiK8fo8u xArxpiRWVqUW5ccXleakFh9iNAUG5ERmKdHkfGA6ziuJNzQ1MjY2tjAxMzczNVYS5+X4cTFW SCA9sSQ1OzW1ILUIpo+Jg1OqgbH/oXr+b1OO2pQZqiZ8aju2rd7EGmi4/KfP+b6C5Rek6wsq LkyJUHg2757KDNWU3AWPfu+eWPLL7xR3wYNlyXoq14JMHXtUOpj/T3tzV7Ihr4HlYuuxbRxR tVnnr7x/45slpPE5aDfv9OPvhU/9d68JPNBgJNGv2tTdL/mm6Jnyq81X9+lEqyqxFGckGmox FxUnAgBFwH0FjwMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrGLMWRmVeSWpSXmKPExsWy7bCSnK7dtMuxBu1/2SyaF69ns1i5+iiT xfW7t5gt9uw9yWJxedccNov/s56zWvyYXm+x5d8RVotL7z+wOHB67Jx1l91j/9w17B67bzaw efRtWcXo8XmTnMeh7W/YPG4/28YSwB7FZZOSmpNZllqkb5fAlfGg/SNrQZtbxd6F51gaGFdb dDFyckgImEg8OTSVvYuRi0NIYDejRGfLbVaIhLTEsRNnmLsYOYBsYYnDh4shaj4wSnRfmcoE EmcT0Jb4s0UUpFxEwFGid9dhFpAaZpA5W6b/YgRJCAsYSHyefY0NxGYRUJW4uHQxO4jNK2At sfL2Q0aIXfISqzccYAaxOQVsJPrnfQerEQKq2bxoCesERr4FjAyrGCVTC4pz03OLDQsM81LL 9YoTc4tL89L1kvNzNzGCQ1NLcwfj5SXxhxgFOBiVeHhPqFyOFWJNLCuuzD3EKMHBrCTC6/fo QqwQb0piZVVqUX58UWlOavEhRmkOFiVx3qd5xyKFBNITS1KzU1MLUotgskwcnFINjMJvZ06Z raB2/e7cpqbs9baN620aRFfvF746X2PhvdO/7j8oaTxWurp9/X+Nnb3NzzM7MvmtfR6/+cu3 6h7nubXn/tVOnsghu5N3plRLZudnK/FOxg9sDCVrDQ9yePRLM/zV3rRc5HrOSaup6TFH34Yf aCgTL3hdp+0bsP9GaPJ077Bpms8U/ymxFGckGmoxFxUnAgCluQGISQIAAA== X-CMS-MailID: 20191119071406epcas1p34788f32abd876190f2b0a5c2ba39e4b1 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119071406epcas1p34788f32abd876190f2b0a5c2ba39e4b1 References: <20191119071107.1947-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 07:11:03 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11250991 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 737B0184E for ; Tue, 19 Nov 2019 07:14:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 495CA222DF for ; Tue, 19 Nov 2019 07:14:55 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="rOgp0Wuf" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727823AbfKSHOq (ORCPT ); Tue, 19 Nov 2019 02:14:46 -0500 Received: from mailout1.samsung.com ([203.254.224.24]:41623 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727443AbfKSHOL (ORCPT ); Tue, 19 Nov 2019 02:14:11 -0500 Received: from epcas1p1.samsung.com (unknown [182.195.41.45]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20191119071408epoutp015c4830b612b35967abe9058f0087277c~YfvI7-rqc1765217652epoutp015 for ; Tue, 19 Nov 2019 07:14:08 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20191119071408epoutp015c4830b612b35967abe9058f0087277c~YfvI7-rqc1765217652epoutp015 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574147648; bh=XpygHjuf8d7CE3zc20VcrTtJAW5giJFyIx169VoIE/Y=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rOgp0WufgSvsLTLG9bfCgM30QjNAuMtK+pBiN1xcO60zNY2ilAKcyYPBzqAcgyELA HQTjyqNvx8a4rIlSclIyiSzwArvUjvlXaz+MSBYDBkEc4Ubo9OgNbKf0JNoPhXuOni LFIqURCbx8aiFCveimTdDmbYMv9d1qSPqdSjYkvo= Received: from epsnrtp4.localdomain (unknown [182.195.42.165]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20191119071408epcas1p10588b44ac2652e04cf61a0d640d0d3c9~YfvIh87M70158901589epcas1p13; Tue, 19 Nov 2019 07:14:08 +0000 (GMT) Received: from epsmges1p1.samsung.com (unknown [182.195.40.164]) by epsnrtp4.localdomain (Postfix) with ESMTP id 47HHB70v3WzMqYkb; Tue, 19 Nov 2019 07:14:07 +0000 (GMT) Received: from epcas1p1.samsung.com ( [182.195.41.45]) by epsmges1p1.samsung.com (Symantec Messaging Gateway) with SMTP id 11.7B.04072.F3693DD5; Tue, 19 Nov 2019 16:14:07 +0900 (KST) Received: from epsmtrp2.samsung.com (unknown [182.195.40.14]) by epcas1p2.samsung.com (KnoxPortal) with ESMTPA id 20191119071406epcas1p285f075eac966cfdd6f79362ecc433d6b~YfvHHYoD41561515615epcas1p22; Tue, 19 Nov 2019 07:14:06 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp2.samsung.com (KnoxPortal) with ESMTP id 20191119071406epsmtrp25e932c7e7e61cc78092e03a2fb6c22d9~YfvHGt4Qb0193901939epsmtrp2U; Tue, 19 Nov 2019 07:14:06 +0000 (GMT) X-AuditID: b6c32a35-e16a59c000000fe8-ad-5dd3963f297c Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id EC.14.03654.E3693DD5; Tue, 19 Nov 2019 16:14:06 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191119071406epsmtip14052a654d776eb92f3434bb26d4ff4ec~YfvG8oQOb1281112811epsmtip1f; Tue, 19 Nov 2019 07:14:06 +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, Namjae Jeon Subject: [PATCH v2 09/13] exfat: add misc operations Date: Tue, 19 Nov 2019 02:11:03 -0500 Message-Id: <20191119071107.1947-10-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119071107.1947-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrNKsWRmVeSWpSXmKPExsWy7bCmrq79tMuxBpf+sVo0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD0qxyYjNTEltUghNS85PyUzL91WyTs43jne1MzAUNfQ 0sJcSSEvMTfVVsnFJ0DXLTMH6DIlhbLEnFKgUEBicbGSvp1NUX5pSapCRn5xia1SakFKToGh QYFecWJucWleul5yfq6VoYGBkSlQZUJORuOSHYwFVywrtt5pY29gPKXTxcjBISFgItGxLKmL kYtDSGAHo8TPg+/YIZxPjBKPv61k7GLkBHK+MUocfhoKYoM0bJj0iw2iaC+jxPRbz1jgOv78 2MoOMpZNQFvizxZRkAYRAXuJzbMPgNUwC2xmlHi4aSkLSEIYaNKESTfANrAIqEq0PmkGi/MK 2Ejs/fmMCWKbvMTqDQeYQWxOoHj/vO9g50kI7GGTWHLlLwtEkYvElo4fbBC2sMSr41vYIWwp ic/v9rJB/Fkt8XE/M0S4g1HixXdbCNtY4ub6DawgJcwCmhLrd+lDhBUldv6eC3YaswCfxLuv PawQU3glOtqEIEpUJfouHYa6Ulqiq/0D1FIPiRPz9rNCgqSfUeLMuyvMExjlZiFsWMDIuIpR LLWgODc9tdiwwBA5vjYxghOelukOxinnfA4xCnAwKvHwKqhfjhViTSwrrsw9xCjBwawkwuv3 6EKsEG9KYmVValF+fFFpTmrxIUZTYEBOZJYSTc4HJuO8knhDUyNjY2MLEzNzM1NjJXFejh8X Y4UE0hNLUrNTUwtSi2D6mDg4pRoYD25s2GgRKW1f7sZxuuuFej6TjPiUghaH2N0LC34+69jy ICyJfa21qWNnU/Ye7l8zVA/IvL0+P6Na898L7fWnddYpn9oe9fLC4w/3WASY7z2NeJHh5/qp 5fTcv2KJ6W4rv93gWFt2jGlPTHn36ze6Putm/Pu+ymnq8RfWEqofnC0FHu3MCviRpMRSnJFo qMVcVJwIAO8VLmaOAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrGLMWRmVeSWpSXmKPExsWy7bCSnK7dtMuxBn0XlC2aF69ns1i5+iiT xfW7t5gt9uw9yWJxedccNov/s56zWvyYXm+x5d8RVotL7z+wOHB67Jx1l91j/9w17B67bzaw efRtWcXo8XmTnMeh7W/YPG4/28YSwB7FZZOSmpNZllqkb5fAldG4ZAdjwRXLiq132tgbGE/p dDFyckgImEhsmPSLrYuRi0NIYDejxMpDV9ghEtISx06cYe5i5ACyhSUOHy6GqPnAKPFm6QxW kDibgLbEny2iIOUiAo4SvbsOs4DUMIPM2TL9FyNIQhhowYRJN8BsFgFVidYnzSwgNq+AjcTe n8+YIHbJS6zecIAZxOYEivfP+w52g5CAtcTmRUtYJzDyLWBkWMUomVpQnJueW2xYYJiXWq5X nJhbXJqXrpecn7uJERyaWpo7GC8viT/EKMDBqMTDe0LlcqwQa2JZcWXuIUYJDmYlEV6/Rxdi hXhTEiurUovy44tKc1KLDzFKc7AoifM+zTsWKSSQnliSmp2aWpBaBJNl4uCUamDMr5nqKCCT tPJ00aZpzLtsrmidz3VdHP/JwKXa6P8R0Sk/lnKuKbSP9BDonmqu8k2jOFk7UOD5fKPvb1+o /ma91W92ybsqJk7HUq4/+stnjbY+CX3eq2F2S55JTZm55VGobNqNh3WBa++mm/E6xYT1ryzl rLyW8KBnxh3hT573WLrvL7L1UlRiKc5INNRiLipOBAAblr6ISQIAAA== X-CMS-MailID: 20191119071406epcas1p285f075eac966cfdd6f79362ecc433d6b X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119071406epcas1p285f075eac966cfdd6f79362ecc433d6b References: <20191119071107.1947-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 07:11:04 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11250979 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 33A77138C for ; Tue, 19 Nov 2019 07:14:29 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E6FF42230C for ; Tue, 19 Nov 2019 07:14:28 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="NhT7ifSv" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727670AbfKSHOR (ORCPT ); Tue, 19 Nov 2019 02:14:17 -0500 Received: from mailout1.samsung.com ([203.254.224.24]:41593 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727059AbfKSHOQ (ORCPT ); Tue, 19 Nov 2019 02:14:16 -0500 Received: from epcas1p2.samsung.com (unknown [182.195.41.46]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20191119071410epoutp01cdb0fcd437b3f1b0b65253f4b123c92f~YfvKrKw9r1765217652epoutp017 for ; Tue, 19 Nov 2019 07:14:10 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20191119071410epoutp01cdb0fcd437b3f1b0b65253f4b123c92f~YfvKrKw9r1765217652epoutp017 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574147650; bh=sg6rBMV6EJtwTkWZmrtWVl0lnSXUM5YyRcAV+lePukM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NhT7ifSvf3YIB9mIcrxQ4md9PVps0drO+RY+KnURINILmMVsRZDJHx0B92T2IS8x5 8keXUCXzk4988fOdDw4R8kzmaUqLgdwp2ZAWJnkHaLZKVTSmYSRvKMhfdCt56BlrNz 7dfuFdWDb9QSQtZVFnVEhccI0YoUyMV6WB8NyABU= Received: from epsnrtp1.localdomain (unknown [182.195.42.162]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20191119071409epcas1p1e051603525d21b7a308a9b2267bc7f2a~YfvKCMc0S0158901589epcas1p17; Tue, 19 Nov 2019 07:14:09 +0000 (GMT) Received: from epsmges1p1.samsung.com (unknown [182.195.40.163]) by epsnrtp1.localdomain (Postfix) with ESMTP id 47HHB75PbDzMqYlh; Tue, 19 Nov 2019 07:14:07 +0000 (GMT) Received: from epcas1p3.samsung.com ( [182.195.41.47]) by epsmges1p1.samsung.com (Symantec Messaging Gateway) with SMTP id A3.7B.04072.F3693DD5; Tue, 19 Nov 2019 16:14:07 +0900 (KST) Received: from epsmtrp2.samsung.com (unknown [182.195.40.14]) by epcas1p4.samsung.com (KnoxPortal) with ESMTPA id 20191119071407epcas1p4af1dc25ff22a70050b87f82be4cdf731~YfvHtVzvZ1344213442epcas1p4y; Tue, 19 Nov 2019 07:14:07 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp2.samsung.com (KnoxPortal) with ESMTP id 20191119071407epsmtrp2bad20372f64701938ebc8b061496a30f~YfvHskxS00193901939epsmtrp2V; Tue, 19 Nov 2019 07:14:07 +0000 (GMT) X-AuditID: b6c32a35-9bdff70000000fe8-b6-5dd3963fa807 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id 9D.84.03814.F3693DD5; Tue, 19 Nov 2019 16:14:07 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191119071407epsmtip1fdeb3d67dd6540965ee7064038c1801f~YfvHgvQY-1409814098epsmtip1B; Tue, 19 Nov 2019 07:14:07 +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, Namjae Jeon Subject: [PATCH v2 10/13] exfat: add nls operations Date: Tue, 19 Nov 2019 02:11:04 -0500 Message-Id: <20191119071107.1947-11-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119071107.1947-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrDKsWRmVeSWpSXmKPExsWy7bCmvq79tMuxBu8eaFs0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD0qxyYjNTEltUghNS85PyUzL91WyTs43jne1MzAUNfQ 0sJcSSEvMTfVVsnFJ0DXLTMH6DIlhbLEnFKgUEBicbGSvp1NUX5pSapCRn5xia1SakFKToGh QYFecWJucWleul5yfq6VoYGBkSlQZUJOxoKN21kKfs1kqti7sJmtgfHlS8YuRk4OCQETiS9t 51i6GLk4hAR2MErMu/qYGcL5xCix+c9xqMw3RomVF5+wwLQcbF7EBJHYyyhxf0UvO1zLrV+/ gKo4ONgEtCX+bBEFaRARsJfYPPsA2CRmgc2MEg83LQWrERYwlpi4uwSkhkVAVWLBwr1gC3gF bCQ+vF4KtUxeYvWGA8wgNidQvH/ed3aI+B42iaVLyiFsF4ljHw9BxYUlXh3fAmVLSXx+t5cN ZJWEQLXEx/3MEOEORokX320hbGOJm+s3sIKUMAtoSqzfpQ8RVpTY+XsuOISYBfgk3n3tYYWY wivR0SYEUaIq0XfpMBOELS3R1f6BHaLEQ2LnDz9IePQzSlxvuM8ygVFuFsKCBYyMqxjFUguK c9NTiw0LDJEjbBMjOOVpme5gnHLO5xCjAAejEg+vgvrlWCHWxLLiytxDjBIczEoivH6PLsQK 8aYkVlalFuXHF5XmpBYfYjQFBuNEZinR5HxgOs4riTc0NTI2NrYwMTM3MzVWEufl+HExVkgg PbEkNTs1tSC1CKaPiYNTqoGxXEXk3CHVYs3Kz3b7+660/T33dernlkVsAdOuec/ie7eXxXHF 5CSnjbrcu1v19M8fb/oXo7PkzJd58k8lr0WbshZMZSr0U4+r0rxyuIp1N9MJk8vuonfFLpwo vBGtp3G2PKHhnw6fF2/roUvW33vOntiy7YNYo/jG8MXTns7Uuv5j/vOHF+ckKbEUZyQaajEX FScCAJLWUhKPAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrOLMWRmVeSWpSXmKPExsWy7bCSnK79tMuxBi3TeSyaF69ns1i5+iiT xfW7t5gt9uw9yWJxedccNov/s56zWvyYXm+x5d8RVotL7z+wOHB67Jx1l91j/9w17B67bzaw efRtWcXo8XmTnMeh7W/YPG4/28YSwB7FZZOSmpNZllqkb5fAlbFg43aWgl8zmSr2Lmxma2B8 +ZKxi5GTQ0LAROJg8yKmLkYuDiGB3YwSF/5eZIZISEscO3EGyOYAsoUlDh8uhqj5wCgx9/sq sDibgLbEny2iIOUiAo4SvbsOs4DUMIPM2TL9FyNIjbCAscTE3SUgNSwCqhILFu5lAbF5BWwk PrxeygKxSl5i9YYDYGs5geL9876zg9hCAtYSmxctYZ3AyLeAkWEVo2RqQXFuem6xYYFRXmq5 XnFibnFpXrpecn7uJkZwcGpp7WA8cSL+EKMAB6MSD+8JlcuxQqyJZcWVuYcYJTiYlUR4/R5d iBXiTUmsrEotyo8vKs1JLT7EKM3BoiTOK59/LFJIID2xJDU7NbUgtQgmy8TBKdXAaOfo9XyW 3iG7skyffquqK52mSn5WD9/P2cc6pbShvWqOxL5pum2fL5wX+5C052Fw0YT/vTWLQyQmJrR7 2pVuuGFdUnw+wWuzbaxvvnzzzwttKhoevPtW9zOKCUgxFblfvcLB35bS0LWEQabV4s1taedg w6UbrDJLlki2ZPUWBKs8+rMsKleJpTgj0VCLuag4EQBjJP+6SgIAAA== X-CMS-MailID: 20191119071407epcas1p4af1dc25ff22a70050b87f82be4cdf731 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119071407epcas1p4af1dc25ff22a70050b87f82be4cdf731 References: <20191119071107.1947-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 07:11:05 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11250977 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 0FAE4138C for ; Tue, 19 Nov 2019 07:14:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D97E8222DF for ; Tue, 19 Nov 2019 07:14:27 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="fgE4msI5" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727698AbfKSHOS (ORCPT ); Tue, 19 Nov 2019 02:14:18 -0500 Received: from mailout2.samsung.com ([203.254.224.25]:62639 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727575AbfKSHOP (ORCPT ); Tue, 19 Nov 2019 02:14:15 -0500 Received: from epcas1p1.samsung.com (unknown [182.195.41.45]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20191119071411epoutp02ad8468f899d328d6169cdc429b5ed120~YfvL4TLXy1270812708epoutp02P for ; Tue, 19 Nov 2019 07:14:11 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20191119071411epoutp02ad8468f899d328d6169cdc429b5ed120~YfvL4TLXy1270812708epoutp02P DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574147651; bh=W/nvY8vh7b5VKEGm7/5P+NKNPdIjQbHkTzk7gijW0ro=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=fgE4msI5KEWw7wEpcgjpQ3zYC1E7/5zKlj7S5SGGpKzRY0Mtt0aimZuRncNdVIxLT TRBfkVH/ck6ie3dUhnTm9scRRcYgWsmmD1jJ72LuOyTQBcB+oHKZsGnsibKFOP/d4f R23LZejk4LDv7qEQR8RVHol6i6BKLuOoactWMg9o= Received: from epsnrtp2.localdomain (unknown [182.195.42.163]) by epcas1p2.samsung.com (KnoxPortal) with ESMTP id 20191119071411epcas1p2416d86ee55e6b22b3aed5de25f9ca934~YfvLdyEFK1339313393epcas1p2W; Tue, 19 Nov 2019 07:14:11 +0000 (GMT) Received: from epsmges1p5.samsung.com (unknown [182.195.40.163]) by epsnrtp2.localdomain (Postfix) with ESMTP id 47HHB85C9PzMqYkk; Tue, 19 Nov 2019 07:14:08 +0000 (GMT) Received: from epcas1p4.samsung.com ( [182.195.41.48]) by epsmges1p5.samsung.com (Symantec Messaging Gateway) with SMTP id CA.E8.04237.04693DD5; Tue, 19 Nov 2019 16:14:08 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p3.samsung.com (KnoxPortal) with ESMTPA id 20191119071408epcas1p355692e5e4b48c7c08617974715ae636d~YfvIwPL0N2873728737epcas1p3J; Tue, 19 Nov 2019 07:14:08 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119071408epsmtrp17bf358218355f6e78c3ce1e0abf1e33a~YfvIvoqvK3109231092epsmtrp1A; Tue, 19 Nov 2019 07:14:08 +0000 (GMT) X-AuditID: b6c32a39-913ff7000000108d-31-5dd39640a660 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id ED.14.03654.04693DD5; Tue, 19 Nov 2019 16:14:08 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191119071408epsmtip1d023d2b9d60ee038189be442732b6d36~YfvInZF7T1409814098epsmtip1D; Tue, 19 Nov 2019 07:14:08 +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, Namjae Jeon Subject: [PATCH v2 11/13] exfat: add Kconfig and Makefile Date: Tue, 19 Nov 2019 02:11:05 -0500 Message-Id: <20191119071107.1947-12-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119071107.1947-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA01Sa0hTYRju85yznUmLw7L8MMp5IELL3HFNj6ZlZHVCCaGgKGQd3GGTdmNn muaPJMOGVMsoC7tQSWHesbE275hd1S4aiDcqqdQys6J0Ztbm0erf8z7f87zvw/u9OCL7iQXh 6UYrZzGyelLkjzrvhSrCE4q6UxWOjjA6r6RaRN8uv+9H9wz2IXRD42OU7q67LKJ/Fw9j9NSF o7Rjtg2juz5PoAkSxl08KGaar1SImfreXBFz2lEGmG+1q5jWu2Mipv+9E00R79fH6ThWw1nk nDHNpEk3auPJpN3qrWpVlIIKp2LoaFJuZA1cPJmYnBK+PV3vTUbKM1l9hpdKYXmejNgUZzFl WDm5zsRb40nOrNGbKYV5Pc8a+Ayjdn2ayRBLKRSRKq/yoF7XNrbTbJNmnZnJQ3NBl38BkOCQ 2AAd44VYAfDHZYQLwIlbPWKh+Apgfef5+eIHgJWl4+iCpcrhQoSHRgA7+6ZFfy3Oyot+BQDH RcRaOONY5jMEEJvhnUstqE+DEHcAfFN7c67TUiIGNhZdBD6MEqvhqOsV5sNSIg6OfWwXC9OC YXlNC+LDEi9vvzo5FwkSLhEcmp71E0SJsP/TVyDgpfDDQ8e8OQiO2vPFvkCQyIFfmhGBtgE4 MhkvYCXsra7BfBKECIXVdRECHQLdP6/MdUSIJXD8+0lM6CKFtnyZIFkNT3fdmw+wAhacmJgf xEC387CwETuAXTYPcgasKv434BoAZWA5Z+YNWo6nzKr//6sWzB1eWIwLPHia3AoIHJCLpfI1 3akyjM3ksw2tAOIIGSDdNfQ8VSbVsNlHOItJbcnQc3wrUHn3WIgELUszec/YaFVTqkilUklv iIqOUinJQCk+9SJVRmhZK3eI48ycZcHnh0uCcoF7f9aWovbmQWRllSf4/oA2qenwTAdVCm2B T04tL/nYnb73WGFZVHHbr5V5zREjhTs877Lb0dBFVJUtS7buGTH7oDpsyPlsX9HZ14/XuRH3 cXsp02DPwZK3zRzH8s8hnhBJcIpi28ZX33UHHjUF7rkenJ9YoR5wx4o9LxM0N94OkyivY6kw xMKzfwCKw+6qjgMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrKLMWRmVeSWpSXmKPExsWy7bCSnK7DtMuxBme2ilk0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD2KyyYlNSezLLVI3y6BK+PIG8+CDt6KCX+aWRoYL3F1 MXJySAiYSKzbsoO5i5GLQ0hgN6PE5t4v7BAJaYljJ84AJTiAbGGJw4eLIWo+MEp8bd4PFmcT 0Jb4s0UUpFxEwFGid9dhFpAaZpA5W6b/YgRJCAtYSuydNgPMZhFQlXi54z4riM0rYCPx5vVp qF3yEqs3HGAGsTmB4v3zvoPFhQSsJTYvWsI6gZFvASPDKkbJ1ILi3PTcYsMCw7zUcr3ixNzi 0rx0veT83E2M4MDU0tzBeHlJ/CFGAQ5GJR7eEyqXY4VYE8uKK3MPMUpwMCuJ8Po9uhArxJuS WFmVWpQfX1Sak1p8iFGag0VJnPdp3rFIIYH0xJLU7NTUgtQimCwTB6dUAyO/zHr9isPT1S3W 6G3fKHtTfGZs5or/7zbYt85m6XjmmTY3oXfv0ned6kYPW7adt9qcq1fhsSJgx33H2W3PtyvL yX/pVe0IsuBqvCFya0J+xfnqBb3tF51XchhXNSZLuj4/4HRpmVfDPzv5Y+vXzZxbeUSwt8Hy h0nSwbfxBypVzh48zK7qyqXEUpyRaKjFXFScCACILC+bSAIAAA== X-CMS-MailID: 20191119071408epcas1p355692e5e4b48c7c08617974715ae636d X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119071408epcas1p355692e5e4b48c7c08617974715ae636d References: <20191119071107.1947-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 07:11: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: 11250973 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 715096C1 for ; Tue, 19 Nov 2019 07:14:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 5081E2230C for ; Tue, 19 Nov 2019 07:14:17 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="P+y6AiGO" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727628AbfKSHON (ORCPT ); Tue, 19 Nov 2019 02:14:13 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:35562 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727557AbfKSHON (ORCPT ); Tue, 19 Nov 2019 02:14:13 -0500 Received: from epcas1p2.samsung.com (unknown [182.195.41.46]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20191119071410epoutp04b9529d88ba86cfe37af305053dfa8064~YfvLG8XjX1949019490epoutp04K for ; Tue, 19 Nov 2019 07:14:10 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20191119071410epoutp04b9529d88ba86cfe37af305053dfa8064~YfvLG8XjX1949019490epoutp04K DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574147650; bh=LzsUvoRQ+/EvYb4sCIgQeQ8VbnU5fmhs4hyN/5tdGrs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=P+y6AiGORcGsx9AtPegn3HiyMTXEjKsKYv7zLaBiRnwrxEQoRaZ5ZUCTVtt1h7/VW F1Sz2A5vetwlkoUTgCYEb1fBXKe/65NeVRRHeLpc+0vXFbZxCOiQ6BHHxrM4sF9Tyi 2TE+vqnKdP3vWQCqXxmaAlTbD2Qk6HgMTp8F7s4o= Received: from epsnrtp1.localdomain (unknown [182.195.42.162]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20191119071410epcas1p1f977186847bf941799e18723dc7c206a~YfvKzcM2I3222032220epcas1p1a; Tue, 19 Nov 2019 07:14:10 +0000 (GMT) Received: from epsmges1p2.samsung.com (unknown [182.195.40.162]) by epsnrtp1.localdomain (Postfix) with ESMTP id 47HHB959BmzMqYm0; Tue, 19 Nov 2019 07:14:09 +0000 (GMT) Received: from epcas1p2.samsung.com ( [182.195.41.46]) by epsmges1p2.samsung.com (Symantec Messaging Gateway) with SMTP id 80.3C.04235.14693DD5; Tue, 19 Nov 2019 16:14:09 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p1.samsung.com (KnoxPortal) with ESMTPA id 20191119071409epcas1p1b2464462c7972c11ae8719528f0c43a8~YfvJcJl6u0158901589epcas1p16; Tue, 19 Nov 2019 07:14:09 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191119071409epsmtrp1bebabae489c5894fe68d7fb51a37d2c1~YfvJbgiTu3109231092epsmtrp1B; Tue, 19 Nov 2019 07:14:09 +0000 (GMT) X-AuditID: b6c32a36-defff7000000108b-9f-5dd396411b95 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id DE.14.03654.14693DD5; Tue, 19 Nov 2019 16:14:09 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191119071408epsmtip1759fcf86aa0480b04ad146c83afcd70a~YfvJOoaJC1409814098epsmtip1E; Tue, 19 Nov 2019 07:14:08 +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, Namjae Jeon Subject: [PATCH v2 12/13] exfat: add exfat in fs/Kconfig and fs/Makefile Date: Tue, 19 Nov 2019 02:11:06 -0500 Message-Id: <20191119071107.1947-13-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119071107.1947-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrNKsWRmVeSWpSXmKPExsWy7bCmnq7jtMuxBvsnq1k0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD0qxyYjNTEltUghNS85PyUzL91WyTs43jne1MzAUNfQ 0sJcSSEvMTfVVsnFJ0DXLTMH6DIlhbLEnFKgUEBicbGSvp1NUX5pSapCRn5xia1SakFKToGh QYFecWJucWleul5yfq6VoYGBkSlQZUJOxqTPSxkLlnFUHF27nbmB8T1bFyMnh4SAicSCk/dZ uxi5OIQEdjBK/LvbyAjhfGKU+PnsFFTmG6PE73+zWWBaGvdsZIdI7GWUOL7gAwtcy6TjR4H6 OTjYBLQl/mwRBWkQEbCX2Dz7AFgNs8BmRomHm5aCTRIW8JB4c3UaI4jNIqAq0dvzjRnE5hWw kbjx/ioTxDZ5idUbDoDFOYHi/fO+g22WENjDJnH97yKgQexAjovEpXyIcmGJV8e3sEPYUhIv +9vYQc6REKiW+LifGSLcwSjx4rsthG0scXP9BlaQEmYBTYn1u/QhwooSO3/PBTuMWYBP4t3X HlaIKbwSHW1CECWqEn2XDkPdKC3R1f4BaqmHxJJbk6EB0s8o8f3uVZYJjHKzEDYsYGRcxSiW WlCcm55abFhghBxfmxjBCU/LbAfjonM+hxgFOBiVeHhPqFyOFWJNLCuuzD3EKMHBrCTC6/fo QqwQb0piZVVqUX58UWlOavEhRlNgME5klhJNzgcm47ySeENTI2NjYwsTM3MzU2MlcV6OHxdj hQTSE0tSs1NTC1KLYPqYODilGhjXrZxylZNpp/wWFmuRVfNlri9Y3efQtNbOvWu+nES6Q83H N2J/1W02nf94XsDrOfOpy1/+7DdovmJxL+TBFw6egw8qDlc/yeR69ct4zrp3Zy/rFYrkduxg vlzxbYOo7BQHh4WCs7/3bV3YKGY1Veoiw0WmpQlLPy/ZEVzu5/my3ORzjF3b7lQ3JZbijERD Leai4kQAOOfIrI4DAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrGLMWRmVeSWpSXmKPExsWy7bCSnK7jtMuxBnN/MVs0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD2KyyYlNSezLLVI3y6BK2PS56WMBcs4Ko6u3c7cwPie rYuRk0NCwESicc9G9i5GLg4hgd2MEmeWLmGHSEhLHDtxhrmLkQPIFpY4fLgYouYDo8Tqh3MY QeJsAtoSf7aIgpSLCDhK9O46zAJSwwwyZ8v0X4wgCWEBD4k3V6eB2SwCqhK9Pd+YQWxeARuJ G++vMkHskpdYveEAWJwTKN4/7zvYDUIC1hKbFy1hncDIt4CRYRWjZGpBcW56brFhgWFearle cWJucWleul5yfu4mRnBoamnuYLy8JP4QowAHoxIP7wmVy7FCrIllxZW5hxglOJiVRHj9Hl2I FeJNSaysSi3Kjy8qzUktPsQozcGiJM77NO9YpJBAemJJanZqakFqEUyWiYNTqoGRQcqtkquv a/PrvXOUuFetmX/V5HCq9GTnY+Vc5zy6rs9istv/VOTIzeS79lFPfy5V14taWLriFsO7gnxG ryP7D8wJFJdRTNIP08gublIsqbG8veaRkPbCZ8v1plYfUvLyvKdeccXiw/Pjlw7ZLVzpvMr/ 8PdUxbc913t8fV9NvC8z00axfsMnJZbijERDLeai4kQAEQkQu0kCAAA= X-CMS-MailID: 20191119071409epcas1p1b2464462c7972c11ae8719528f0c43a8 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119071409epcas1p1b2464462c7972c11ae8719528f0c43a8 References: <20191119071107.1947-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 07:11: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: 11250975 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 3311E6C1 for ; Tue, 19 Nov 2019 07:14:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 12DF72230C for ; Tue, 19 Nov 2019 07:14:18 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="FH9GH42X" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727450AbfKSHOR (ORCPT ); Tue, 19 Nov 2019 02:14:17 -0500 Received: from mailout2.samsung.com ([203.254.224.25]:62630 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727566AbfKSHON (ORCPT ); Tue, 19 Nov 2019 02:14:13 -0500 Received: from epcas1p2.samsung.com (unknown [182.195.41.46]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20191119071411epoutp020cd3f3c966c892acd2f4c0696a9b5438~YfvLiAt571289712897epoutp021 for ; Tue, 19 Nov 2019 07:14:11 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20191119071411epoutp020cd3f3c966c892acd2f4c0696a9b5438~YfvLiAt571289712897epoutp021 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574147651; bh=qOKAH/xLKFcgxQkDaZUSWSDe5GuU0Alfu+G9AfD13f4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=FH9GH42Xy5r+mOGp0O4zBpybTjJ828xae9rwSuzj+KyTJHI78Gef24l7XdoMrhacy yjhw1uDmh7K5n9KcQI+LRoBGVOAAmJqzjE7KUfsfAEEy4FvDjgViYmW333pC1WZ9LX iWNkbLUddco9qcTS0VWVAiMmubSaMZQF5FAsF5e0= Received: from epsnrtp3.localdomain (unknown [182.195.42.164]) by epcas1p4.samsung.com (KnoxPortal) with ESMTP id 20191119071410epcas1p4141b13c7535abd1e63cb4240fbd5d671~YfvK_IR540775207752epcas1p4v; Tue, 19 Nov 2019 07:14:10 +0000 (GMT) Received: from epsmges1p2.samsung.com (unknown [182.195.40.165]) by epsnrtp3.localdomain (Postfix) with ESMTP id 47HHBB1cbKzMqYkY; Tue, 19 Nov 2019 07:14:10 +0000 (GMT) Received: from epcas1p3.samsung.com ( [182.195.41.47]) by epsmges1p2.samsung.com (Symantec Messaging Gateway) with SMTP id D1.3C.04235.24693DD5; Tue, 19 Nov 2019 16:14:10 +0900 (KST) Received: from epsmtrp2.samsung.com (unknown [182.195.40.14]) by epcas1p2.samsung.com (KnoxPortal) with ESMTPA id 20191119071409epcas1p2253bc4b3be05ac82201126bc62bd37ac~YfvKCSU_A1338013380epcas1p2-; Tue, 19 Nov 2019 07:14:09 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp2.samsung.com (KnoxPortal) with ESMTP id 20191119071409epsmtrp2e7fc92826931d38d7d565461e4b2ef82~YfvKBp3Mx0193901939epsmtrp2Z; Tue, 19 Nov 2019 07:14:09 +0000 (GMT) X-AuditID: b6c32a36-e07ff7000000108b-a6-5dd396428540 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id 3E.84.03814.14693DD5; Tue, 19 Nov 2019 16:14:09 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191119071409epsmtip1b222ae39fb2bdfefd7f5d14083eea99b~YfvJ4X5Pd1281112811epsmtip1h; Tue, 19 Nov 2019 07:14:09 +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, Namjae Jeon Subject: [PATCH v2 13/13] MAINTAINERS: add exfat filesystem Date: Tue, 19 Nov 2019 02:11:07 -0500 Message-Id: <20191119071107.1947-14-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191119071107.1947-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFvrBKsWRmVeSWpSXmKPExsWy7bCmvq7TtMuxBlf38Vg0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD0qxyYjNTEltUghNS85PyUzL91WyTs43jne1MzAUNfQ 0sJcSSEvMTfVVsnFJ0DXLTMH6DIlhbLEnFKgUEBicbGSvp1NUX5pSapCRn5xia1SakFKToGh QYFecWJucWleul5yfq6VoYGBkSlQZUJOxpUrG5kLvrFW3Gxcy9TA2MDaxcjBISFgIvFocmEX IxeHkMAORolnO9rZIZxPjBI/Dl5hgXC+MUqsv7kIrmPpMsYuRk6g+F5GiUuNxRA2UMPuU7wg JWwC2hJ/toiChEUE7CU2zz4ANoZZYDOjxMNNS1lAEsIC1hIbjp9jBbFZBFQlGpfNZATp5RWw kThzMRMkLCEgL7F6wwFmEJsTKNw/7zvYbRICO9gk9s78yQxR5CIx69k/KFtY4tXxLewQtpTE 53d72SBOrpb4uB+qpINR4sV3WwjbWOLm+g1gXzELaEqs36UPEVaU2Pl7LtiHzAJ8Eu++9kA9 zivR0SYEUaIq0XfpMBOELS3R1f4BaqmHxO07KxghgdbPKNH6djvTBEa5WQgbFjAyrmIUSy0o zk1PLTYsMEKOrE2M4FSnZbaDcdE5n0OMAhyMSjy8J1QuxwqxJpYVV+YeYpTgYFYS4fV7dCFW iDclsbIqtSg/vqg0J7X4EKMpMBwnMkuJJucD03BeSbyhqZGxsbGFiZm5mamxkjgvx4+LsUIC 6YklqdmpqQWpRTB9TBycUg2M2V1TZCaf0dLbwf+TQ7/o3KYNKXkT9adfaG1f4Z+xro9DlWHD ia1mLWunzDF7Xage6izT1R7gYTmLPWJFha7c+VadZUsKuI+o8mqWMuxd2FKpcemum9e2Zz+c Tm64zFTqaF6YUTFxo8bjA+93JjgxVSarv5mut9HhvNff4JXTRNc1bFs/7VGiEktxRqKhFnNR cSIA8wuB7osDAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrCLMWRmVeSWpSXmKPExsWy7bCSnK7jtMuxBlOO6Fk0L17PZrFy9VEm i+t3bzFb7Nl7ksXi8q45bBb/Zz1ntfgxvd5iy78jrBaX3n9gceD02DnrLrvH/rlr2D1232xg 8+jbsorR4/MmOY9D29+wedx+to0lgD2KyyYlNSezLLVI3y6BK+PKlY3MBd9YK242rmVqYGxg 7WLk4JAQMJFYuoyxi5GLQ0hgN6PEpgWbgRxOoLi0xLETZ5ghaoQlDh8uhqj5wCix8PUjFpA4 m4C2xJ8toiDlIgKOEr27DrOA1DCDzNky/RfYHGEBa4kNx8+xgtgsAqoSjctmMoL08grYSJy5 mAmxSl5i9YYDzCA2J1C4f953dhBbCKh186IlrBMY+RYwMqxilEwtKM5Nzy02LDDKSy3XK07M LS7NS9dLzs/dxAgOSy2tHYwnTsQfYhTgYFTi4T2hcjlWiDWxrLgy9xCjBAezkgiv36MLsUK8 KYmVValF+fFFpTmpxYcYpTlYlMR55fOPRQoJpCeWpGanphakFsFkmTg4pRoYLaN5wlxsMybt iH3H05S09VBVuhTb9dUHXjl8W1S9xr0odsqZA7e/+6y+la14veOGzfumMgveGwsaI1o+pM3O OLjic7x7jm5C+qIHrleNbPfM8Jgo07IluNM670fe3vcaL3L2dfRedrh+5u85qeTDs38dtmy7 MXcKx9MLLdvEXwd5BM1aU7y4V4mlOCPRUIu5qDgRAIgf8BNHAgAA X-CMS-MailID: 20191119071409epcas1p2253bc4b3be05ac82201126bc62bd37ac X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191119071409epcas1p2253bc4b3be05ac82201126bc62bd37ac References: <20191119071107.1947-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