From patchwork Thu Nov 21 05:26: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: 11255319 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 BDF81138C for ; Thu, 21 Nov 2019 05:29:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 780D12089F for ; Thu, 21 Nov 2019 05:29:25 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="j5z0KTTQ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726558AbfKUF3X (ORCPT ); Thu, 21 Nov 2019 00:29:23 -0500 Received: from mailout1.samsung.com ([203.254.224.24]:62312 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725904AbfKUF3W (ORCPT ); Thu, 21 Nov 2019 00:29:22 -0500 Received: from epcas1p2.samsung.com (unknown [182.195.41.46]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20191121052919epoutp01d00a771fc1dcb1f4455e19f7cc4ee337~ZFmL06nrH1281512815epoutp01D for ; Thu, 21 Nov 2019 05:29:19 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20191121052919epoutp01d00a771fc1dcb1f4455e19f7cc4ee337~ZFmL06nrH1281512815epoutp01D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574314159; bh=3zr4+P2+DByscJa2bJn0woI5FNm5jXGHqCX9i9Zg4P4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=j5z0KTTQJJJ6FKcf+JcfwQeTHRrxKio6EJMyvtEDoBo3e1nQi0EdRjhjhGAVapo7K fhKR2hP04hsFmD+TAAsw3ujWRA7vCVwevhC8LV1UlNeefmNDkVGkowZHl7dwIRlfhD 6uPF9mIArEM+/tnWCrwryfMgkQdW1p0ZCMQ8XVN4= Received: from epsnrtp4.localdomain (unknown [182.195.42.165]) by epcas1p2.samsung.com (KnoxPortal) with ESMTP id 20191121052918epcas1p258feb314233990b5de296c391dcbff40~ZFmLUm-7Z0048000480epcas1p2l; Thu, 21 Nov 2019 05:29:18 +0000 (GMT) Received: from epsmges1p3.samsung.com (unknown [182.195.40.162]) by epsnrtp4.localdomain (Postfix) with ESMTP id 47JSmF2mzxzMqYkY; Thu, 21 Nov 2019 05:29:17 +0000 (GMT) Received: from epcas1p2.samsung.com ( [182.195.41.46]) by epsmges1p3.samsung.com (Symantec Messaging Gateway) with SMTP id 66.30.04080.BA026DD5; Thu, 21 Nov 2019 14:29:15 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p3.samsung.com (KnoxPortal) with ESMTPA id 20191121052915epcas1p36613f6c468471c95d02da5db92b5c6cd~ZFmINV9IZ3012130121epcas1p3D; Thu, 21 Nov 2019 05:29:15 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191121052915epsmtrp1d6a3b964f84059be571d3169f2057f12~ZFmIMn26U1320813208epsmtrp1r; Thu, 21 Nov 2019 05:29:15 +0000 (GMT) X-AuditID: b6c32a37-7cdff70000000ff0-c9-5dd620ab1947 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id FC.88.03814.AA026DD5; Thu, 21 Nov 2019 14:29:14 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191121052914epsmtip135810a5da79accd17cec89c0b80daf1f~ZFmH_suCO1142611426epsmtip11; Thu, 21 Nov 2019 05:29:14 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, nborisov@suse.com, Namjae Jeon Subject: [PATCH v4 01/13] exfat: add in-memory and on-disk structures and headers Date: Thu, 21 Nov 2019 00:26:06 -0500 Message-Id: <20191121052618.31117-2-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191121052618.31117-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA01SWUwTURTN60zbQalOCuhLVayjKEvA1lIYDXVFM1ESiegP2OAExpbYjU5B URNQFC0xUA2JshkVI6EY2Rr2irbWNdGIC4shbhHEoOKOwa1lEP0775x77nnvvosh4mKBBMsw WBizgdYRgmloszssKrJW+lgta7aFke6XJ4RkflWdgKyp9fDInoF+hOx03kLJB+0VAvJ32RCf HDuZS/4eOYSSjl/X+GT3+1F09XSqrWxASHVVXhRSHX15AqrIYQdUneMRSjXd2Ud9agymXC0j AurJYDOa6Jesi9MydDpjljKGNGN6hkGjIjYlpa5LVcbI5JHy5WQsITXQekZFxCckRm7I0Hkv S0izaV2Wl0qkWZZYujLObMyyMFKtkbWoCMaUrjPJZaYoltazWQZNVJpRv0Iuky1Teit36LSv 3EWIqfcq2DPWfRfkgXtnQCHwwyAeDa8ftPIKwTRMjLcC+KapTMgdPgJY3WGbVL4C6OwdmrI8 b7vM5wQngNaaPmTK8uzmBa+CYQI8Av5wBPkMgfgq2FR+BfXVIHgPgIP9p/g+IQBPgo8bziM+ jOIhsKbk/oRXhMdBz+dgLmw+rK2/gvhoP1wFn47n+NpAvEcA7b3VKFcTD615pTwOB8A3NxxC DkvgcHGB0OeF+D74oQvh6KMAvv6m4rAC9tXVT6QieBisa1/K0Qtg23jlxHMRfAZ89+UYn+si gkcLxFxJCCzqdk+GzoGFR0YngyhorZrLzcMGoN1jR2wguOxfwBkA7GAWY2L1GoaVmxT/f1gj mFjG8NhWUH83wQVwDBD+Im3oI7WYT2ezOXoXgBhCBIo6ex6qxaJ0OmcvYzammrN0DOsCSu8U jyOSoDSjd7UNllS5cplCoSCjY2JjlApitggbu68W4xrawuxiGBNj/uvjYX6SPFDIu1G6db0/ 26A+rJccat84XFIbsmK3s966Ntc9GBFzeaErufr0JaH/pyXg3uKtm00z53V9KCjdEqrZNmPn koDvZz0HRjs+dqRcN9gsrauFjeuG5OeGf57udChb8z0vKhax1kzP15boJmdUyvaS/WtF5eGl KRXS2WOxe5VvMzNvF68hUFZLy8MRM0v/AdyNi6uiAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrBLMWRmVeSWpSXmKPExsWy7bCSnO5qhWuxBjuELA4/nsRu0bx4PZvF ytVHmSyu373FbLFn70kWi8u75rBZ/J/1nNXix/R6i/9vWlgstvw7wmpx6f0HFgduj52z7rJ7 7J+7ht1j980GNo++LasYPdZvucrisfl0tcfnTXIeh7a/YfO4/WwbSwBnFJdNSmpOZllqkb5d AlfG08N9zAU3DjJW/Lh0jrGB8fwCxi5GTg4JAROJhzv3sXYxcnEICexmlFiwpY8ZIiEtcezE GSCbA8gWljh8uBii5gOjxLXvK9lA4mwC2hJ/toiClIsIOEr07jrMAlLDLPCYUeLE+SdgC4QF AiU2TO4Hs1kEVCVWTrnICtLLK2AjcfSLHMQqeYnVGw6AreIUsJW4/7sSJCwEVHH1xAvWCYx8 CxgZVjFKphYU56bnFhsWGOWllusVJ+YWl+al6yXn525iBIevltYOxhMn4g8xCnAwKvHwZmhc jRViTSwrrsw9xCjBwawkwrvn+pVYId6UxMqq1KL8+KLSnNTiQ4zSHCxK4rzy+ccihQTSE0tS s1NTC1KLYLJMHJxSDYxRyl2yZ5bt+T6P/5/B+t0ujX4zXmu2LRMLPiMb+ahAflkIj5LmTqkS 1uNfF+kWyBi4H/LunxJ8I/FhikZKgZvwh2KNoA1yh3XUze9uqnDxF/RoZSgwyeMu2lN3paLh y1qPjtVPHtoaZnpp6R+6XvI+/57Xf7eTB1vbDx4tc3ngWBW/ck4EixJLcUaioRZzUXEiAEXB 02xbAgAA X-CMS-MailID: 20191121052915epcas1p36613f6c468471c95d02da5db92b5c6cd X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191121052915epcas1p36613f6c468471c95d02da5db92b5c6cd References: <20191121052618.31117-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 | 538 +++++++++++++++++++++++++++++++++++++++++++ fs/exfat/exfat_raw.h | 194 ++++++++++++++++ 2 files changed, 732 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..ada7afc56c1c --- /dev/null +++ b/fs/exfat/exfat_fs.h @@ -0,0 +1,538 @@ +/* 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 + */ +enum { + NLS_NAME_NO_LOSSY, /* no lossy */ + NLS_NAME_LOSSY, /* just detected incorrect filename(s) */ + NLS_NAME_OVERLEN, /* 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) + +#define EXFAT_SB_DIRTY 0 + +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 */ + + unsigned long s_state; + 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..5f962043f913 --- /dev/null +++ b/fs/exfat/exfat_raw.h @@ -0,0 +1,194 @@ +/* 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 + +/* AllocationPossible and NoFatChain field in GeneralSecondaryFlags Field */ +#define ALLOC_FAT_CHAIN 0x01 +#define ALLOC_NO_FAT_CHAIN 0x03 + +#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 Thu Nov 21 05:26: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: 11255335 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 7C3ED14C0 for ; Thu, 21 Nov 2019 05:30:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3EE1E20898 for ; Thu, 21 Nov 2019 05:30:05 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="a8jBiB71" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726698AbfKUF31 (ORCPT ); Thu, 21 Nov 2019 00:29:27 -0500 Received: from mailout1.samsung.com ([203.254.224.24]:62434 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726408AbfKUF3Z (ORCPT ); Thu, 21 Nov 2019 00:29:25 -0500 Received: from epcas1p1.samsung.com (unknown [182.195.41.45]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20191121052919epoutp01e677f10f8f5ff8b3bed935ad596ac578~ZFmMUgjZ21281512815epoutp01E for ; Thu, 21 Nov 2019 05:29:19 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20191121052919epoutp01e677f10f8f5ff8b3bed935ad596ac578~ZFmMUgjZ21281512815epoutp01E DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574314159; bh=dwLVI3epazAZr9rfOlIoy23fdEXjgvKxx5gnJqYghag=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=a8jBiB71flfKO5eRW7Doh6pEBIErcUfbZGvkpScpAK5DGInXMrDTfgk1l01yI2QnE BL7GaoCShUZg4Pq0e+ELY1ItxGFLVtSonZwtKi6xwzammupMPveMGEEQX/JTJrPD+a RlWmEenP6XdCDwaamjwv6cXUrBE66oXWoPW8uczc= Received: from epsnrtp4.localdomain (unknown [182.195.42.165]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20191121052919epcas1p14e6846428a297de153e6fcb9776ccb10~ZFmL2SK6b0317303173epcas1p1T; Thu, 21 Nov 2019 05:29:19 +0000 (GMT) Received: from epsmges1p2.samsung.com (unknown [182.195.40.164]) by epsnrtp4.localdomain (Postfix) with ESMTP id 47JSmF4t7rzMqYkv; Thu, 21 Nov 2019 05:29:17 +0000 (GMT) Received: from epcas1p2.samsung.com ( [182.195.41.46]) by epsmges1p2.samsung.com (Symantec Messaging Gateway) with SMTP id C5.E7.04235.CA026DD5; Thu, 21 Nov 2019 14:29:16 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p3.samsung.com (KnoxPortal) with ESMTPA id 20191121052915epcas1p30f42f12990926942d24aa514ebc437ac~ZFmI2-ykT3012130121epcas1p3G; Thu, 21 Nov 2019 05:29:15 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191121052915epsmtrp165f750aa5af2a17f908ae8f987b82c26~ZFmI2VN3u1320713207epsmtrp1u; Thu, 21 Nov 2019 05:29:15 +0000 (GMT) X-AuditID: b6c32a36-25fdf9c00000108b-18-5dd620ac8047 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id DD.88.03814.BA026DD5; Thu, 21 Nov 2019 14:29:15 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191121052915epsmtip1e487c321bac9d126daac5c68f81e5c2a~ZFmIoArfb1025410254epsmtip1g; Thu, 21 Nov 2019 05:29:15 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, nborisov@suse.com, Namjae Jeon Subject: [PATCH v4 02/13] exfat: add super block operations Date: Thu, 21 Nov 2019 00:26:07 -0500 Message-Id: <20191121052618.31117-3-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191121052618.31117-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA02Se0hTcRTH+3nv7u6qxWVq/Vhk60ZUhra5Nm/pSsjqQlFSUJTJuriLG+3F vZu4IhIzK6mVRg807SEkWTRpaz5SXFPREgRNM420B/Sml1RqD9u8Wv33+Z3z/Z5zOL+DIzI3 JsdNVgfLWRkziU1H/c1L4+NuKB5mKD+Xz6SaXxSLqUMVHoy6dr01gup7MoBQDY33UOpB/QWM Gi95JaJGzh2kxt/no5Tvd4uI6v74CU2ZQdeVPBHTTWU3xPSd/lyMdvuqAO3x9aK0t2M/PXwr hg7WvMfoxy/9aJpklznZyDIGllOw1kybwWTN0pEbt+nX6jVapSpOtZJKJBVWxsLqyNRNaXHr TebQsKQimzE7Q6E0hufJ5auTOZvTwSqMNt6hI1m7wWxXKe3xPGPhndas+EybZZVKqUzQhJR7 zEaPOx+xB8pBzpi/BeSCsTxQCCQ4JFbA9qraCZYRtQC2d6cWgukh/gLgsdOXIoTHNwCfVnZi U47BavdkojHkqA9gfy2D/WNoIcBxjFgGf/qiw4YoYg30lgbQsAYh+gB8OXBeFE5EEkmwpvIZ EmaUWAQ7vf6JDlIiGboHXk/ONx9erw4g4ZoSQgeHfriE8BAGe67uFDgVNt1rFQkcCd+2+cQC y+GbkwXisBUS++HnJkQIHwXw9XedwGrY76kWhSUIsRR66pcL4QWw7kfZxAAIMQt++HpcJFSR wqMFMkGyCLq7myMEngsLj3yabErDY3mlYmGfpwD03k46BWJK/jW4BEAVmM3aeUsWy6vsCf// 1y0wcYux2lpwpXNTEBA4IGdKjUt6M2QiJpt3WYIA4ggZJW3o68mQSQ2Max/L2fSc08zyQaAJ bbEIkUdn2kKXbXXoVZoEtVpNrdAmajVqco4UH+nKkBFZjIPdy7J2lpvyReASeS4I1I/aVh82 pf8qbaiJ2jBtpHLrza677mvpWx+l8Fefi3LmFv9WDL9ToFK962R6trP33Pkz21+YNsdy23YX bSm/zy8e9SODvD4q+IzYYYncUoGDroHTl7VSOLRxbLfhYuto44G6Blhx9qeLXdgm59ZJbj5P zE4Z9y4LJljnnT3RQaK8kVHFIhzP/AH1VnPwoQMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrFLMWRmVeSWpSXmKPExsWy7bCSnO5qhWuxBquWalkcfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9Rb/37SwWGz5d4TV4tL7DywO3B47Z91l 99g/dw27x+6bDWwefVtWMXqs33KVxWPz6WqPz5vkPA5tf8PmcfvZNpYAzigum5TUnMyy1CJ9 uwSujPV9LcwFB+YxVvzadoSxgfFXE2MXIyeHhICJxL0NfUxdjFwcQgK7GSVWnVoFlZCWOHbi DHMXIweQLSxx+HAxRM0HRon9xy4ygcTZBLQl/mwRBSkXEXCU6N11mAWkhlngMaPEifNPwOYI C1hLbF/+kBnEZhFQlTi3eRsbiM0rYCPRd+sF1C55idUbDoDt4hSwlbj/uxIkLARUcvXEC9YJ jHwLGBlWMUqmFhTnpucWGxYY5aWW6xUn5haX5qXrJefnbmIEh7CW1g7GEyfiDzEKcDAq8fBm aFyNFWJNLCuuzD3EKMHBrCTCu+f6lVgh3pTEyqrUovz4otKc1OJDjNIcLErivPL5xyKFBNIT S1KzU1MLUotgskwcnFINjFlsK154dy5ZZau+dVP98xSN2rk5D3aulm/R2y60WEFtzlmD1s1r SmT4997ujLFw8ujfvMWJmZ3LxcT00VnvA+3sTy7eePhkxRJbj3Qtwa8xhwru+jpyT9/1/sb0 N0sOC7D9Zg9Yns554bmrp8uN68u+Pnl6s8s9/9qLFVw9e25VrzENfdYZEaXEUpyRaKjFXFSc CACQj06LXQIAAA== X-CMS-MailID: 20191121052915epcas1p30f42f12990926942d24aa514ebc437ac X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191121052915epcas1p30f42f12990926942d24aa514ebc437ac References: <20191121052618.31117-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 | 737 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 737 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..ee4f1698aba5 --- /dev/null +++ b/fs/exfat/super.c @@ -0,0 +1,737 @@ +// 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 (test_and_clear_bit(EXFAT_SB_DIRTY, &sbi->s_state)) + 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 (test_and_clear_bit(EXFAT_SB_DIRTY, &sbi->s_state)) { + 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, ALLOC_FAT_CHAIN); + ei->entry = -1; + ei->start_clu = sbi->root_dir; + ei->flags = ALLOC_FAT_CHAIN; + 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, ALLOC_FAT_CHAIN); + 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); + sbi->FAT2_start_sector = p_bpb->bsx.num_fats == 1 ? + sbi->FAT1_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_table; + } + + 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_table: + 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; + + sb->s_d_op = EXFAT_SB(sb)->options.case_sensitive ? + &exfat_dentry_ops : &exfat_ci_dentry_ops; + + err = __exfat_fill_super(sb); + if (err) { + exfat_msg(sb, KERN_ERR, "failed to recognize exfat type"); + goto check_nls_io; + } + + /* 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 free_table; + } + + root_inode = new_inode(sb); + if (!root_inode) { + exfat_msg(sb, KERN_ERR, "failed to allocate root inode."); + err = -ENOMEM; + goto free_table; + } + + 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 put_inode; + } + + 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 put_inode; + } + + return 0; + +put_inode: + iput(root_inode); + sb->s_root = NULL; + +free_table: + exfat_free_upcase_table(sb); + exfat_free_bitmap(sb); + +check_nls_io: + 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 Thu Nov 21 05:26:08 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11255327 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 EE882109A for ; Thu, 21 Nov 2019 05:29:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 9073F20872 for ; Thu, 21 Nov 2019 05:29:46 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="pQl8jFY/" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726948AbfKUF3i (ORCPT ); Thu, 21 Nov 2019 00:29:38 -0500 Received: from mailout1.samsung.com ([203.254.224.24]:62449 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726529AbfKUF33 (ORCPT ); Thu, 21 Nov 2019 00:29:29 -0500 Received: from epcas1p1.samsung.com (unknown [182.195.41.45]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20191121052919epoutp01dccaf696da7f65bfd06086bdafb73f31~ZFmMue7Oe1281512815epoutp01G for ; Thu, 21 Nov 2019 05:29:19 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20191121052919epoutp01dccaf696da7f65bfd06086bdafb73f31~ZFmMue7Oe1281512815epoutp01G DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574314160; bh=yCUio4sXFCzeXH4avCFfAWhIBi18s9RxHeLZJW6e1/c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pQl8jFY/bi6p65z1c6U1FDbCxiwl/N7+zc1Rhi0WaSv1VodiuKbAyrMtfmVjeAfI0 OiDQwCRJZHImWTs4ZfYMRPGoFLHR3PU5448d7kwFZz0mns+elF2h721IVej35aPNuL e4V/kD6Tf1UVIwXKIjvEVyZsFFp+NCsi5XW71CDk= Received: from epsnrtp2.localdomain (unknown [182.195.42.163]) by epcas1p3.samsung.com (KnoxPortal) with ESMTP id 20191121052919epcas1p3465231efb883c958a05524ad941b3c8a~ZFmMRlLL23012130121epcas1p3h; Thu, 21 Nov 2019 05:29:19 +0000 (GMT) Received: from epsmges1p5.samsung.com (unknown [182.195.40.160]) by epsnrtp2.localdomain (Postfix) with ESMTP id 47JSmG2KTBzMqYks; Thu, 21 Nov 2019 05:29:18 +0000 (GMT) Received: from epcas1p1.samsung.com ( [182.195.41.45]) by epsmges1p5.samsung.com (Symantec Messaging Gateway) with SMTP id C2.3A.04237.CA026DD5; Thu, 21 Nov 2019 14:29:16 +0900 (KST) Received: from epsmtrp2.samsung.com (unknown [182.195.40.14]) by epcas1p3.samsung.com (KnoxPortal) with ESMTPA id 20191121052916epcas1p3f00c8e510eb53f53f4e082848bd325d0~ZFmJflu7o3012130121epcas1p3L; Thu, 21 Nov 2019 05:29:16 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp2.samsung.com (KnoxPortal) with ESMTP id 20191121052916epsmtrp26b96f43b240fae3496624db0c7c43bb4~ZFmJeveRI1666416664epsmtrp2X; Thu, 21 Nov 2019 05:29:16 +0000 (GMT) X-AuditID: b6c32a39-913ff7000000108d-23-5dd620ac4d15 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 92.06.03654.CA026DD5; Thu, 21 Nov 2019 14:29:16 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191121052916epsmtip1b4a541a2e5fb17ad4b6d022a8140b122~ZFmJNPAIa0971209712epsmtip1d; Thu, 21 Nov 2019 05:29:16 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, nborisov@suse.com, Namjae Jeon Subject: [PATCH v4 03/13] exfat: add inode operations Date: Thu, 21 Nov 2019 00:26:08 -0500 Message-Id: <20191121052618.31117-4-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191121052618.31117-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA01Sa0hTYRju27l4Zi1O0+rFyuahIrPl5pqdzHVBiRP2Q+hHVIgd9OCk3dqZ I7uQWdQQKasf1Uww11Uty4aZJZrTVMIou4imhIQWGXaRaqVYm2dd/r2X53mf53u/l8KUJ8go KtfiEOwW3sSQ4XidL1atvq56maFp38T63pwKYw97akj2WlWbjO0Z6MPY+42dOPus4TzJ/nK/ JVj/mYPsrw9HcNY72Uqw3R8/4eunc3fdA2FcU1l1GHevt4DkjnsrEVfjfYFztx/t48Zqo7mW Ox9I7tVwHZ4u325KNgp8tmBXCZYsa3auJcfApG3JTMnUJ2q0au1qdhWjsvBmwcCkbk5Xb8w1 BbwyKidvyguU0nlRZOLXJtuteQ5BZbSKDgMj2LJNNq3GtkLkzWKeJWdFltWcpNVoEvQB5E6T sdRdhtmKB7E9N7rWFKAfhVgRklNAr4RqTy9ZhMIpJV2PoKOhi5CSLwienK4LJd8QND9oJv9Q /GNXManRiGBk8DL6S6nvd+FFiKJIOg4mvLODhEh6HdwubcaDGIzuQTDcd5YINiJoPVS7/CgY 4/RiaBu5NWVKQSdDe/0hmaS2EKpuNmPBmXLaAK/H84NzgO4h4dXTl2ESJhVOdp4ipDgC3rd7 Q/UoGBttJINcoPfB56bQm10I3n03SLEOemtuEkEIRsdCTUO8VI6Bu+NlU84weiaMfi0mpCkK cB1VSpDFcLzbFzI5D4qOfQqJcnCxriu00RIEreeeYyUo2v1PoRyhSjRHsInmHEHU2vT//1gt mjrGZavr0cPHm1sQTSFmhsK49EWGkuCdYr65BQGFMZGK+z3PM5SKbD5/r2C3ZtrzTILYgvSB PZ7EomZnWQOnbXFkavUJOp2OXZm4KlGvY+YqKP/TDCWdwzuEXYJgE+x/eDJKHlWA4pxXrsVM +EoqolEWXPLAkyrvEs/QtrhFTakjnusNpcNltz6W9y9w7j1jutA9OTR66Y58a/5IomKw0p0y OBD90zt0zLV8Q1qf8krSjtjlzjE90Xq6Yzda4oxXbZEdLUwpbqtCSbUd/mlq34FZs9IK/eGV 1RUcPjl//3DEzm2LZAwuGnntMswu8r8B9XnlaqIDAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrBLMWRmVeSWpSXmKPExsWy7bCSnO4ahWuxBnsaJCwOP57EbtG8eD2b xcrVR5ksrt+9xWyxZ+9JFovLu+awWfyf9ZzV4sf0eov/b1pYLLb8O8Jqcen9BxYHbo+ds+6y e+yfu4bdY/fNBjaPvi2rGD3Wb7nK4rH5dLXH501yHoe2v2HzuP1sG0sAZxSXTUpqTmZZapG+ XQJXxuxZc5kLeh4yV6w7a93A+LOJuYuRk0NCwETix+cVYLaQwG5GiXeLcyDi0hLHTpwBinMA 2cIShw8XdzFyAZV8YJTYMfM1K0icTUBb4s8WUZByEQFHid5dh1lAapgFHjNKnDj/hBEkISxg KrGm4weYzSKgKnH09UawXbwCNhLHdzQyQeySl1i94QDYLk4BW4n7vyshzrGRuHriBesERr4F jAyrGCVTC4pz03OLDQsM81LL9YoTc4tL89L1kvNzNzGCw1dLcwfj5SXxhxgFOBiVeHgzNK7G CrEmlhVX5h5ilOBgVhLh3XP9SqwQb0piZVVqUX58UWlOavEhRmkOFiVx3qd5xyKFBNITS1Kz U1MLUotgskwcnFINjDNKnta+yje/uGZTdNwP7uT6sNolBgViGn9F3RPmP/YSOivdIH1x48Fj s10OLf3IkqS2+UNG/uv/HRyrJvHrPKm0CvB8+5rLxeLk18WGDpUfOzhZvtSsXZcYyOA/Ofj4 3HXqaUdSY/eqzuXpelRoemCVxk8FttNTNCSMdT6VbRMS01doaxX4qsRSnJFoqMVcVJwIAK+x vbNbAgAA X-CMS-MailID: 20191121052916epcas1p3f00c8e510eb53f53f4e082848bd325d0 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191121052916epcas1p3f00c8e510eb53f53f4e082848bd325d0 References: <20191121052618.31117-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 | 693 +++++++++++++++++++++ fs/exfat/namei.c | 1488 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2181 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..e1e20e74c92a --- /dev/null +++ b/fs/exfat/inode.c @@ -0,0 +1,693 @@ +// 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 */ +enum { + BMAP_NOT_CREATE, + BMAP_ADD_CLUSTER, +}; + +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 == ALLOC_NO_FAT_CHAIN) { + 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 == ALLOC_FAT_CHAIN) + ei->flags = ALLOC_FAT_CHAIN; + 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 = ALLOC_FAT_CHAIN; + modified = true; + } + if (new_clu.flags == ALLOC_FAT_CHAIN) + 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 == ALLOC_NO_FAT_CHAIN) { + *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..e51ca0dd9e04 --- /dev/null +++ b/fs/exfat/namei.c @@ -0,0 +1,1488 @@ +// 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 == ALLOC_NO_FAT_CHAIN) { + 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 = ALLOC_FAT_CHAIN; + hint_femp.cur.flags = ALLOC_FAT_CHAIN; + } + + if (clu.flags == ALLOC_FAT_CHAIN) + 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 = ALLOC_NO_FAT_CHAIN; + 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 = ALLOC_FAT_CHAIN; + 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, ALLOC_FAT_CHAIN); + 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 = ALLOC_NO_FAT_CHAIN; + 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 == ALLOC_NO_FAT_CHAIN) { + 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 = ALLOC_NO_FAT_CHAIN; + } +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 Thu Nov 21 05:26:09 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11255333 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 AF6D6138C for ; Thu, 21 Nov 2019 05:30:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 715D62089F for ; Thu, 21 Nov 2019 05:30:04 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="XUgDjoOT" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727028AbfKUF3y (ORCPT ); Thu, 21 Nov 2019 00:29:54 -0500 Received: from mailout1.samsung.com ([203.254.224.24]:62479 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725854AbfKUF31 (ORCPT ); Thu, 21 Nov 2019 00:29:27 -0500 Received: from epcas1p1.samsung.com (unknown [182.195.41.45]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20191121052920epoutp01097f67c7966b17b0cd82e6ea7c04d7f6~ZFmNR19cS1193211932epoutp01- for ; Thu, 21 Nov 2019 05:29:20 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20191121052920epoutp01097f67c7966b17b0cd82e6ea7c04d7f6~ZFmNR19cS1193211932epoutp01- DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574314160; bh=ia4ZjWDWVrp6xtdAQvAPv3FpnaVP8osK04yz+YEluxk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XUgDjoOTK28IF5eYCW886HJXR4ekxhmLUuEFOE0B2ohSTyneb6fGwnLQJcloYag5t Wzw/dRirtRgI3STMv7AV44F970VyExC7JTMJ7+HuBqA5l+eoVnLdGlLshwZ4uWEaR+ wRdkSOAAJ6bpSXHlDMRwMp8I1CPSZskjfqeZEaFE= Received: from epsnrtp2.localdomain (unknown [182.195.42.163]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20191121052920epcas1p193c3086c8a56bf62fe303479948e103a~ZFmM2Wfud0317303173epcas1p1d; Thu, 21 Nov 2019 05:29:20 +0000 (GMT) Received: from epsmges1p3.samsung.com (unknown [182.195.40.163]) by epsnrtp2.localdomain (Postfix) with ESMTP id 47JSmG191VzMqYkj; Thu, 21 Nov 2019 05:29:18 +0000 (GMT) Received: from epcas1p1.samsung.com ( [182.195.41.45]) by epsmges1p3.samsung.com (Symantec Messaging Gateway) with SMTP id 69.30.04080.DA026DD5; Thu, 21 Nov 2019 14:29:17 +0900 (KST) Received: from epsmtrp2.samsung.com (unknown [182.195.40.14]) by epcas1p2.samsung.com (KnoxPortal) with ESMTPA id 20191121052917epcas1p259b8cb61ab86975cabc0cf4815a8dc38~ZFmJ_FQws0047900479epcas1p2S; Thu, 21 Nov 2019 05:29:17 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp2.samsung.com (KnoxPortal) with ESMTP id 20191121052917epsmtrp2e5ee3f93f799604bc97315513ab32f93~ZFmJ9KA8X1671516715epsmtrp2C; Thu, 21 Nov 2019 05:29:17 +0000 (GMT) X-AuditID: b6c32a37-7b5ff70000000ff0-da-5dd620adb8d1 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id 2F.88.03814.CA026DD5; Thu, 21 Nov 2019 14:29:16 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191121052916epsmtip1ffecae5d5679f520e82ad9cba91db80e~ZFmJxesh_0774707747epsmtip1K; Thu, 21 Nov 2019 05:29:16 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, nborisov@suse.com, Namjae Jeon Subject: [PATCH v4 04/13] exfat: add directory operations Date: Thu, 21 Nov 2019 00:26:09 -0500 Message-Id: <20191121052618.31117-5-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191121052618.31117-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA01Sa0hTYRjm81x2tFaHpfa1oNapIA11c05P5ipI6kj+EKI/gdlBD5u2W+ds kkUkXWWJWAjWshvhjzRx6SrnBc0LVobkraUiosLKMrOyq9Hadhb1732f93me9/3e7yUQWTku J/JNVo43sQYKj0AfdsXExdUpXmYrK35E0l0zlyX0mTv1OH23tieM9kyMIXRr21OUHmquwmmf 4zVGf688RfvmzqK063c3Rg9+WEB3LWPcjgkJ0379noRpGS3GmTJXDWDqXSMo09h3gvncsI7p fDSHM+Peh2hW+EFDmp5j8zhewZlyzXn5Jp2W2rc/Z3eOJlmpilNto1MohYk1cloqPTMrbk++ wT8spShkDTY/lMUKApWwI40326ycQm8WrFqKs+QZLCqlJV5gjYLNpIvPNRtTVUplosbPPGzQ n67ul1gaLoYd8zhn0GIw/gLYQTgBySS4+KwVs4MIQkY2ATh+tSuUfALw/O1hXEy+Ajh77hX+ V9LuPR1itQHYMenGAoWgpLJmgx0QBE5uhb9cUQE4ktwJG691oAE+QnoA9I5dCfJXkdvgkGMw aIqSm+GHgStoIJaSabDmnC9MbLYe1jo7kIBnOKmFk0tFAR9ITuJwquwJKnLSYYujNvSeVfBt r0sixnL4eb4ND2gheQJ+bEdEuATAN9+0YqyGo/VOLEBByBhY35wgwhuge+l60BEhV8D5L6WY 6CKFJedlImUzLBvsCg25FtovLISaMtBTMoiK2ykHsGdhHisH6xz/OtwCoAZEcxbBqOMElUX9 /481gOA1xqY0AWd/ZicgCUAtl+q3jGTLMLZQKDJ2AkggVKS01TOcLZPmsUXHOd6cw9sMnNAJ NP49XkLkUblm/22brDkqTaJaraaTklOSNWpqtZT4PpAtI3WslTvCcRaO/6sLI8LlxSBjY9J0 7NqR/gLf+A7vveGk6tTuifuxz0vL8W/eWZ9y7oDk5pL9V6lBWSbfuaa3EFOlO2Yb17/n0Z9P ByqX2dRNURe3ntz+TrdIEJsyHjzWWe8uTfOPFi+5D5lXxj8/mtvwrqqvIvK4c7ZAe2Nv1VlL 8mK029tzfyr1WktF3XTfMIUKelYVi/AC+wc+UsHmowMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrFLMWRmVeSWpSXmKPExsWy7bCSnO4ahWuxBhcuW1kcfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9Rb/37SwWGz5d4TV4tL7DywO3B47Z91l 99g/dw27x+6bDWwefVtWMXqs33KVxWPz6WqPz5vkPA5tf8PmcfvZNpYAzigum5TUnMyy1CJ9 uwSujKal59gLNnUzVVzf8JilgfH2BcYuRk4OCQETif3Pmli7GLk4hAR2M0qs2beJHSIhLXHs xBnmLkYOIFtY4vDhYoiaD4wSn+bMYAGJswloS/zZIgpSLiLgKNG76zALSA2zwGNGiRPnn4At EBawlLg86xIbiM0ioCrx/iJILycHr4CNxKrW/0wQu+QlVm84ALaLU8BW4v7vSpCwEFDJ1RMv WCcw8i1gZFjFKJlaUJybnltsWGCUl1quV5yYW1yal66XnJ+7iREcwlpaOxhPnIg/xCjAwajE w5uhcTVWiDWxrLgy9xCjBAezkgjvnutXYoV4UxIrq1KL8uOLSnNSiw8xSnOwKInzyucfixQS SE8sSc1OTS1ILYLJMnFwSjUwGvRcZ1Dul5OIeD5/v+tJnll3Qy9FnZ8Wc6UysTiY+dI8By9P vVQB58IK5rsq8Q1GN36w//ix2JQvcn/W3Dr+6smLj1T+ehGdbetjOM/w08F0Dx/JbTFF93iO TlE45pCkrvur5V/ZE1tlPx2TA35tPAkZxk9vbj5v1OYmfqY1cxejlsl+qyAlluKMREMt5qLi RABZYr7VXQIAAA== X-CMS-MailID: 20191121052917epcas1p259b8cb61ab86975cabc0cf4815a8dc38 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191121052917epcas1p259b8cb61ab86975cabc0cf4815a8dc38 References: <20191121052618.31117-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 | 1335 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1335 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..cbe2acc4ca92 --- /dev/null +++ b/fs/exfat/dir.c @@ -0,0 +1,1335 @@ +// 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, ALLOC_FAT_CHAIN); + 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 == ALLOC_NO_FAT_CHAIN) { + 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 == ALLOC_NO_FAT_CHAIN) { + 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, ALLOC_NO_FAT_CHAIN); + + 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) ? ALLOC_FAT_CHAIN : ALLOC_NO_FAT_CHAIN; + + /* + * 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 == ALLOC_NO_FAT_CHAIN) + 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 == ALLOC_NO_FAT_CHAIN) { + 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; + + num_entries = type == ES_ALL_ENTRIES ? ep->file_num_ext + 1 : 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 == ALLOC_NO_FAT_CHAIN) + 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; + +} + +enum { + DIRENT_STEP_FILE, + DIRENT_STEP_STRM, + DIRENT_STEP_NAME, + DIRENT_STEP_SECD, +}; + +/* + * 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 == ALLOC_NO_FAT_CHAIN) { + 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 == ALLOC_NO_FAT_CHAIN) { + 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 + break; + } + 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 == ALLOC_NO_FAT_CHAIN) { + 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 Thu Nov 21 05:26:10 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11255315 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 B738214DB for ; Thu, 21 Nov 2019 05:29:24 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 858332089F for ; Thu, 21 Nov 2019 05:29:24 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="tLntm5Vv" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726568AbfKUF3X (ORCPT ); Thu, 21 Nov 2019 00:29:23 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:58231 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725854AbfKUF3W (ORCPT ); Thu, 21 Nov 2019 00:29:22 -0500 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20191121052920epoutp042d7bdd97740feb6e5ea781d8e3ae3444~ZFmMygNPt2288822888epoutp04Z for ; Thu, 21 Nov 2019 05:29:19 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20191121052920epoutp042d7bdd97740feb6e5ea781d8e3ae3444~ZFmMygNPt2288822888epoutp04Z DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574314160; bh=KG8rtHr01c/wX/l/muQmDmnvGz4hOdBm+aNgbGp6Jvk=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=tLntm5VvYfK03H3Th86+qr59bRpsg/cdTP00wEJX4CIwi+HIF0n+YsF7Ft069/pb0 d/Dpxy7tc/IH3PFqeReQjfiFF1YOVbEhf/1uizklc6U2+hIzCxaHR+IcJlTLsxgCcn RTIscI7UNVgeBVlUr3Xss4oibklUfQzne6/VaceQ= Received: from epsnrtp3.localdomain (unknown [182.195.42.164]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20191121052919epcas1p1d6641cfd73c8464c5f362f9aa26eba22~ZFmMKmSxv0317303173epcas1p1X; Thu, 21 Nov 2019 05:29:19 +0000 (GMT) Received: from epsmges1p1.samsung.com (unknown [182.195.40.161]) by epsnrtp3.localdomain (Postfix) with ESMTP id 47JSmG241JzMqYkk; Thu, 21 Nov 2019 05:29:18 +0000 (GMT) Received: from epcas1p1.samsung.com ( [182.195.41.45]) by epsmges1p1.samsung.com (Symantec Messaging Gateway) with SMTP id 0F.8C.04072.DA026DD5; Thu, 21 Nov 2019 14:29:17 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p1.samsung.com (KnoxPortal) with ESMTPA id 20191121052917epcas1p1f81875dcc2d1a64dc3420bedc68fb3ba~ZFmKhfipS0314903149epcas1p1O; Thu, 21 Nov 2019 05:29:17 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191121052917epsmtrp1b011c223fee7eaf199ce5f5845076665~ZFmKgybc51320713207epsmtrp1z; Thu, 21 Nov 2019 05:29:17 +0000 (GMT) X-AuditID: b6c32a35-9a5ff70000000fe8-aa-5dd620ad3fbd Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 44.06.03654.DA026DD5; Thu, 21 Nov 2019 14:29:17 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191121052917epsmtip161d025b53d5ccbd4fffb92729ddbbe8e~ZFmKUgZQe1311613116epsmtip1Z; Thu, 21 Nov 2019 05:29:17 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, nborisov@suse.com, Namjae Jeon Subject: [PATCH v4 05/13] exfat: add file operations Date: Thu, 21 Nov 2019 00:26:10 -0500 Message-Id: <20191121052618.31117-6-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191121052618.31117-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFupjk+LIzCtJLcpLzFFi42LZdlhTV3etwrVYgxkt1haHH09it2hevJ7N YuXqo0wW1+/eYrbYs/cki8XlXXPYLP7Pes5q8WN6vcX/Ny0sFlv+HWG1uPT+A4sDt8fOWXfZ PfbPXcPusftmA5tH35ZVjB7rt1xl8dh8utrj8yY5j0Pb37B53H62jSWAMyrHJiM1MSW1SCE1 Lzk/JTMv3VbJOzjeOd7UzMBQ19DSwlxJIS8xN9VWycUnQNctMwfoWCWFssScUqBQQGJxsZK+ nU1RfmlJqkJGfnGJrVJqQUpOgaFBgV5xYm5xaV66XnJ+rpWhgYGRKVBlQk7GntN/WQum+lb8 //mfqYHxjW0XIweHhICJRP8Jri5GLg4hgR2MEm3ftrNCOJ8YJd5P7WKHcL4xSlw7fBAowwnW 8bj/NxNEYi+jxL7Ly1jgWjbMOckOMpdNQFvizxZRkAYRAXuJzbMPgNUwC1xnlHh2awbYJGGg Sa0br7CA2CwCqhLPZ09mBrF5BWwktrduZ4fYJi+xesMBZpCZnAK2Evd/V4LMkRC4zSbRduUy I0SNi8Txeb1MELawxKvjW6B6pSRe9rexQ/xZLfFxPzNEuINR4sV3WwjbWOLm+g2sICXMApoS 63fpQ4QVJXb+ngs2nVmAT+Ld1x5WiCm8Eh1tQhAlqhJ9lw5DLZWW6Gr/ALXUQ+LjjwfQEJnA KDF3+wzWCYxysxA2LGBkXMUollpQnJueWmxYYIgcX5sYwWlRy3QH45RzPocYBTgYlXh4MzSu xgqxJpYVV+YeYpTgYFYS4d1z/UqsEG9KYmVValF+fFFpTmrxIUZTYDhOZJYSTc4Hpuy8knhD UyNjY2MLEzNzM1NjJXFejh8XY4UE0hNLUrNTUwtSi2D6mDg4pRoYi6VySm52S//qENS7XRVb Yrz0elfc5adBOq5fmqNnHzQ/bHBx7nbB5Y6P4zaGi/Lf2LPvaNGF6LUHF92SkTvM1LBph+L6 FMuvjOt15/KkyWrsbni0+QGXjYdJdTDTU6+Pm+3umnDWnGu7v1ulq9qhIqjnPLvA91Wmokd3 LJN58v7Roczsel5mJZbijERDLeai4kQA0ftlCqEDAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrBLMWRmVeSWpSXmKPExsWy7bCSnO5ahWuxBsv75S0OP57EbtG8eD2b xcrVR5ksrt+9xWyxZ+9JFovLu+awWfyf9ZzV4sf0eov/b1pYLLb8O8Jqcen9BxYHbo+ds+6y e+yfu4bdY/fNBjaPvi2rGD3Wb7nK4rH5dLXH501yHoe2v2HzuP1sG0sAZxSXTUpqTmZZapG+ XQJXxp7Tf1kLpvpW/P/5n6mB8Y1tFyMnh4SAicTj/t9MXYxcHEICuxklJk+awAaRkJY4duIM cxcjB5AtLHH4cDFEzQdGiQ09exhB4mwC2hJ/toiClIsIOEr07jrMAlLDLPCYUeLE+SeMIAlh oAWtG6+wgNgsAqoSz2dPZgaxeQVsJLa3bmeH2CUvsXrDAbBdnAK2Evd/V4KEhYBKrp54wTqB kW8BI8MqRsnUguLc9NxiwwLDvNRyveLE3OLSvHS95PzcTYzg8NXS3MF4eUn8IUYBDkYlHt4M jauxQqyJZcWVuYcYJTiYlUR491y/EivEm5JYWZValB9fVJqTWnyIUZqDRUmc92nesUghgfTE ktTs1NSC1CKYLBMHp1QDY/CMt1vaPq3sZpKqPn31sf6veS8994aePH1zwYV7lS3B22cLVcrH zVNeYnSY1ZNP3kdALc68LNO6njXK/t6H+19O5q9w87/dUFAXKHfxwozkr0tyub37zz6+/LjH 0+Lf1W3fpNU+LDM//etktp9EczYX69t8L7WHosnHA96vnbTCOt1M+236JyWW4oxEQy3mouJE AEduHNNbAgAA X-CMS-MailID: 20191121052917epcas1p1f81875dcc2d1a64dc3420bedc68fb3ba X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191121052917epcas1p1f81875dcc2d1a64dc3420bedc68fb3ba References: <20191121052618.31117-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 | 343 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 343 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..52438e8e2e2f --- /dev/null +++ b/fs/exfat/file.c @@ -0,0 +1,343 @@ +// 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 bool 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; + + mask = (S_ISREG(i_mode) || S_ISLNK(i_mode)) ? + sbi->options.fs_fmask : 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 == ALLOC_NO_FAT_CHAIN) { + 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 = ALLOC_NO_FAT_CHAIN; + 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 = ALLOC_FAT_CHAIN; + 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 == ALLOC_FAT_CHAIN && 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 Thu Nov 21 05:26:11 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11255341 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 8FB7014C0 for ; Thu, 21 Nov 2019 05:30:22 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 53E3C20898 for ; Thu, 21 Nov 2019 05:30:22 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="XuqnqScp" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727104AbfKUFaH (ORCPT ); Thu, 21 Nov 2019 00:30:07 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:58250 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725842AbfKUF3Y (ORCPT ); Thu, 21 Nov 2019 00:29:24 -0500 Received: from epcas1p1.samsung.com (unknown [182.195.41.45]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20191121052921epoutp0421dbbb8a918e22168eb8f04794dd5eee~ZFmOJ3KVc2288822888epoutp04c for ; Thu, 21 Nov 2019 05:29:21 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20191121052921epoutp0421dbbb8a918e22168eb8f04794dd5eee~ZFmOJ3KVc2288822888epoutp04c DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574314161; bh=q+DS758cICJ9MN7R5oiW9hdAyzmeIFAdgs2e0Psslu8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XuqnqScpfoOYHy/ZDuTA5W4y2wgEQDhyIF7z1oqIzHYVu1jVFZoFRemEHoyBAcmYJ oEOxBgpygcKUDLFCpbIDm/3emEr0SSy1jL6uw1jyMT148ZWov7azB7vUCSjDaAv/GP 6z73AIe7hYL3KYEVEW/fO2MuoyF6oYsjSFD137gg= Received: from epsnrtp4.localdomain (unknown [182.195.42.165]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20191121052921epcas1p1faa03449b245cabf93f71be352c0623c~ZFmN1ZSE00315303153epcas1p1c; Thu, 21 Nov 2019 05:29:21 +0000 (GMT) Received: from epsmges1p5.samsung.com (unknown [182.195.40.162]) by epsnrtp4.localdomain (Postfix) with ESMTP id 47JSmJ10NwzMqYkf; Thu, 21 Nov 2019 05:29:20 +0000 (GMT) Received: from epcas1p4.samsung.com ( [182.195.41.48]) by epsmges1p5.samsung.com (Symantec Messaging Gateway) with SMTP id 85.3A.04237.EA026DD5; Thu, 21 Nov 2019 14:29:18 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p2.samsung.com (KnoxPortal) with ESMTPA id 20191121052918epcas1p284be629c57ced93afe88b94a35437cb2~ZFmLFhGwZ3070730707epcas1p2h; Thu, 21 Nov 2019 05:29:18 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191121052918epsmtrp180d27d903eb1c535685d6463d4940141~ZFmLEz6ah1320813208epsmtrp1x; Thu, 21 Nov 2019 05:29:18 +0000 (GMT) X-AuditID: b6c32a39-913ff7000000108d-31-5dd620aee97b Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id C0.98.03814.EA026DD5; Thu, 21 Nov 2019 14:29:18 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191121052917epsmtip1aa939bec21308f7d2224b35f1df328ef~ZFmK36xNw1041710417epsmtip19; Thu, 21 Nov 2019 05:29:17 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, nborisov@suse.com, Namjae Jeon Subject: [PATCH v4 06/13] exfat: add exfat entry operations Date: Thu, 21 Nov 2019 00:26:11 -0500 Message-Id: <20191121052618.31117-7-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191121052618.31117-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFupmk+LIzCtJLcpLzFFi42LZdljTQHedwrVYg7YuGYvDjyexWzQvXs9m sXL1USaL63dvMVvs2XuSxeLyrjlsFv9nPWe1+DG93uL/mxYWiy3/jrBaXHr/gcWB22PnrLvs HvvnrmH32H2zgc2jb8sqRo/1W66yeGw+Xe3xeZOcx6Htb9g8bj/bxhLAGZVjk5GamJJapJCa l5yfkpmXbqvkHRzvHG9qZmCoa2hpYa6kkJeYm2qr5OIToOuWmQN0rJJCWWJOKVAoILG4WEnf zqYov7QkVSEjv7jEVim1ICWnwNCgQK84Mbe4NC9dLzk/18rQwMDIFKgyISdj78J37AWPoyu6 2ywbGJ+5dzFyckgImEg82PWCtYuRi0NIYAejxK6vixhBEkICnxglDuyxgLC/MUocvxML0/Dq 8zIWiIa9jBKPbqxlh3CAGlp/NTB1MXJwsAloS/zZIgrSICJgL7F59gGwBmaB64wSz27NYAWp ERawlth7OhKkhkVAVaLp6WNWEJtXwEZi5vHTbBDL5CVWbzjADFLOKWArcf93JcgYCYHrbBIv j1xlgahxkTjQ0w1lC0u8Or6FHcKWknjZ38YO0ishUC3xcT8zRLiDUeLFd1sI21ji5voNYNcw C2hKrN+lDxFWlNj5ey44GJgF+CTefe1hhZjCK9HRJgRRoirRd+kwE4QtLdHV/gFqqYfEha4l bJAAmcAocXX1IqYJjHKzEDYsYGRcxSiWWlCcm55abFhgihxZmxjBCVHLcgfjsXM+hxgFOBiV eHgzNK7GCrEmlhVX5h5ilOBgVhLh3XP9SqwQb0piZVVqUX58UWlOavEhRlNgOE5klhJNzgcm 67ySeENTI2NjYwsTM3MzU2MlcV6OHxdjhQTSE0tSs1NTC1KLYPqYODilGhg3XT4ctvj5v/f3 t0QGGHGqqGWuE5gUcLueZeUL0wvt8+RXLr7JarlTs2eZBNO2PNmT1z92aasLFxd7SXD9EdVh r7i6e8umpvNfc8NuOZavl76rw/xeZ0pYvle3QHtkrPUJ7uYrX4q4f3Qesj3AuHCmfpKqadry Y7GB4vvDTWOMTbRvzJ8nEqHEUpyRaKjFXFScCAC99OmOngMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrKLMWRmVeSWpSXmKPExsWy7bCSnO46hWuxBmuOcVgcfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9Rb/37SwWGz5d4TV4tL7DywO3B47Z91l 99g/dw27x+6bDWwefVtWMXqs33KVxWPz6WqPz5vkPA5tf8PmcfvZNpYAzigum5TUnMyy1CJ9 uwSujL0L37EXPI6u6G6zbGB85t7FyMkhIWAi8erzMpYuRi4OIYHdjBKLe7vZIBLSEsdOnGHu YuQAsoUlDh8uBgkLCXxglNjaJgwSZhPQlvizRRQkLCLgKNG76zDYGGaBx4wSJ84/YQSpERaw lth7OhKkhkVAVaLp6WNWEJtXwEZi5vHTUJvkJVZvOAC2iVPAVuL+70qITTYSV0+8YJ3AyLeA kWEVo2RqQXFuem6xYYFRXmq5XnFibnFpXrpecn7uJkZw4Gpp7WA8cSL+EKMAB6MSD2+GxtVY IdbEsuLK3EOMEhzMSiK8e65fiRXiTUmsrEotyo8vKs1JLT7EKM3BoiTOK59/LFJIID2xJDU7 NbUgtQgmy8TBKdXAWDZ/pmiEyNUstTO9S82z3jCGHZvkE7K6Tj/Aok0vi6090GeL7pMLyY/c tFsWSC5RD3HlmRwyy/Nx1ZOA9b9TBQ7wF5moClg9iF34MnNawdctV5zdGPh2ca/77bnlOP+E ee43xWzS9wZ/Ed/aJVBfK6tVunpTl270P/Yz64VThaQn5i59Uq+ixFKckWioxVxUnAgApa/D NlgCAAA= X-CMS-MailID: 20191121052918epcas1p284be629c57ced93afe88b94a35437cb2 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191121052918epcas1p284be629c57ced93afe88b94a35437cb2 References: <20191121052618.31117-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 | 474 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 474 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..dc5f38fc56fe --- /dev/null +++ b/fs/exfat/fatent.c @@ -0,0 +1,474 @@ +// 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; + } + + set_bit(EXFAT_SB_DIRTY, &sbi->s_state); + clu = p_chain->dir; + + if (p_chain->flags == ALLOC_NO_FAT_CHAIN) { + 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 == ALLOC_NO_FAT_CHAIN) { + *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 release_bhs; + } + 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 release_bhs; + } + + for (i = 0; i < n; i++) + brelse(bhs[i]); + n = 0; + } + } + + if (IS_DIRSYNC(dir)) { + err = exfat_sync_bhs(bhs, n); + if (err) + goto release_bhs; + } + + for (i = 0; i < n; i++) + brelse(bhs[i]); + + return 0; + +release_bhs: + 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 == ALLOC_NO_FAT_CHAIN) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, + num_clusters)) + return -EIO; + p_chain->flags = ALLOC_FAT_CHAIN; + } + } + + set_bit(EXFAT_SB_DIRTY, &sbi->s_state); + + 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 == ALLOC_NO_FAT_CHAIN) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, + num_clusters)) { + ret = -EIO; + goto free_cluster; + } + p_chain->flags = ALLOC_FAT_CHAIN; + } + + /* update allocation bitmap */ + if (exfat_set_bitmap(inode, new_clu - BASE_CLUSTER)) { + ret = -EIO; + goto free_cluster; + } + + num_clusters++; + + /* update FAT table */ + if (p_chain->flags == ALLOC_FAT_CHAIN) { + if (exfat_ent_set(sb, new_clu, EOF_CLUSTER)) { + ret = -EIO; + goto free_cluster; + } + } + + if (p_chain->dir == EOF_CLUSTER) { + p_chain->dir = new_clu; + } else if (p_chain->flags == ALLOC_FAT_CHAIN) { + if (exfat_ent_set(sb, last_clu, new_clu)) { + ret = -EIO; + goto free_cluster; + } + } + 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 == ALLOC_NO_FAT_CHAIN) { + if (exfat_chain_cont_cluster(sb, p_chain->dir, + num_clusters)) { + ret = -EIO; + goto free_cluster; + } + p_chain->flags = ALLOC_FAT_CHAIN; + } + } + } +free_cluster: + 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 == ALLOC_NO_FAT_CHAIN) { + *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 Thu Nov 21 05:26:12 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11255323 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 31180138C for ; Thu, 21 Nov 2019 05:29:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id F20892089D for ; Thu, 21 Nov 2019 05:29:36 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="INABp1vU" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726833AbfKUF3a (ORCPT ); Thu, 21 Nov 2019 00:29:30 -0500 Received: from mailout1.samsung.com ([203.254.224.24]:62454 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726634AbfKUF32 (ORCPT ); Thu, 21 Nov 2019 00:29:28 -0500 Received: from epcas1p1.samsung.com (unknown [182.195.41.45]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20191121052921epoutp014abd216293324fea0d790cf0a11436ec~ZFmODSAhU1241312413epoutp01f for ; Thu, 21 Nov 2019 05:29:21 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20191121052921epoutp014abd216293324fea0d790cf0a11436ec~ZFmODSAhU1241312413epoutp01f DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574314161; bh=RtpjVDaWLsFj//FQ3s03FyYysJjApJaPpaoU1aZx3WI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=INABp1vUla0z4PQgmJEPa/zVCbt9GWDdyDjrv+/s91HnjrBZU9DSJXNyYWDr/4EcQ bHlrwkTh/LCq6ZZdbqs9LEjW0ZZW5SPYjnDRGTXxf3Tm6dKZHo0DvyZaujOxYRqP2S xMqtanwM/l7NrdAQHj+JemjhrBsLQEye3wnWap6c= Received: from epsnrtp3.localdomain (unknown [182.195.42.164]) by epcas1p4.samsung.com (KnoxPortal) with ESMTP id 20191121052920epcas1p4e72a456ebf4196c734a8540e56c16022~ZFmNgNh7m1333513335epcas1p4V; Thu, 21 Nov 2019 05:29:20 +0000 (GMT) Received: from epsmges1p2.samsung.com (unknown [182.195.40.160]) by epsnrtp3.localdomain (Postfix) with ESMTP id 47JSmH50h3zMqYkw; Thu, 21 Nov 2019 05:29:19 +0000 (GMT) Received: from epcas1p1.samsung.com ( [182.195.41.45]) by epsmges1p2.samsung.com (Symantec Messaging Gateway) with SMTP id 79.E7.04235.FA026DD5; Thu, 21 Nov 2019 14:29:19 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p3.samsung.com (KnoxPortal) with ESMTPA id 20191121052918epcas1p31b6aa77d4642ea390e8390bc782d590e~ZFmLpWCLc3089030890epcas1p30; Thu, 21 Nov 2019 05:29:18 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191121052918epsmtrp1ba3863cc540c4da35058b6e63e5e7269~ZFmLokt5R1320813208epsmtrp10; Thu, 21 Nov 2019 05:29:18 +0000 (GMT) X-AuditID: b6c32a36-e07ff7000000108b-2a-5dd620afa816 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 16.06.03654.EA026DD5; Thu, 21 Nov 2019 14:29:18 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191121052918epsmtip1a75e800f0d638debaeb735f95b07292d~ZFmLbZETR1143211432epsmtip1v; Thu, 21 Nov 2019 05:29:18 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, nborisov@suse.com, Namjae Jeon Subject: [PATCH v4 07/13] exfat: add bitmap operations Date: Thu, 21 Nov 2019 00:26:12 -0500 Message-Id: <20191121052618.31117-8-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191121052618.31117-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA01Sa0hTYRjm85ydHa3FYWp9GNo6IWmy2sXNU7gSGnEgQcMfQSB20JOTdmNn hhaUYqXONJWyssJSgrzQvAwz0yzXhUosbw21C5HMLtMyqSxF23bs8u95n+953+fh/V4cEZdh YXiW0cpajIyexILQdme0VGqXvEiVnbggpJzvKoVUQZ0do+obHwRQrldjCNXV/RilhjovYdRS 9aSAmjt3jFryHEcpx+J9ATX4+QuasIK+Vf1KSPdcbhLSt0fzMLrM0QBou2MEpdueHqFnWyPo 3psejB53t6PJgfv08TqWyWAtEtaYbsrIMmZqyN0paTvTVGqZXCrfSsWREiNjYDWkNjFZuitL 7w1LSg4x+mwvlcxwHLlle7zFlG1lJToTZ9WQrDlDb5bLzJs5xsBlGzM3p5sM2+QymULlVe7X 6ybbRoTmUm1Oe8M9YR7oU9pAIA6JWPjzqhOxgSBcTHQAWFnlwfjiK4Afx4oBX3wHcNZm88pw f8vAqJTnuwF09eWjfzuaqpaAT4QRMXDBEeqzCCF2wLaLd/0ahHAB6B47L/A9BBNqWLl4xo9R IhK6HndgPiwi4uHDum7A51sHG5vv+o0DCQ18M5/rmwOJcQzWjD9b1mjh5AO3kMfB8OMjxzIO gx9OnxTyoY/AmR6Ep4sAfP9Dw2MlHLU3C3wShIiG9s4tPL0e3pq/7J+OEKvg9LdTAn6KCBad FPOSSFg26Azg8VpoK/yybErD4vO1yzssB7DEU4OWg4jqfw5XAGgAq1kzZ8hkOblZ8f9/tQL/ LW5Sd4Da/sReQOCAXCnSRY2kigXMIS7X0AsgjpAhoi7XcKpYlMHkHmYtpjRLtp7leoHKu8cK JCw03eS9bKM1Ta5SKJVKKlYdp1YpyTUifG4gVUxkMlb2IMuaWcufvgA8MCwPhHtiTh/4PnNt oqWMcoe/LNbE5lSkjE3lb5g/Wj+48dnzPQGv051aVWFJgfoXrQhxr6syn82p6U8c6Wv+ecdu y6tNGmh5Oxv9ZOt1xpTQr1VwCuWNDtnu4alr6qjRT+WVZHA9W5LgjFm1V7V0HRdMD/fHXhgq Da2YOBa+EJ2UJCZRTsfINyEWjvkND6DwK6EDAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrOLMWRmVeSWpSXmKPExsWy7bCSnO46hWuxBtMWalkcfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9Rb/37SwWGz5d4TV4tL7DywO3B47Z91l 99g/dw27x+6bDWwefVtWMXqs33KVxWPz6WqPz5vkPA5tf8PmcfvZNpYAzigum5TUnMyy1CJ9 uwSujOebr7IX9LpUbFt1kL2B8YxxFyMHh4SAicTFm7pdjFwcQgK7GSWuX//N2sXICRSXljh2 4gwzRI2wxOHDxRA1HxglNk56BxZnE9CW+LNFFKRcRMBRonfXYRaQGmaBx4wSJ84/YQRJCAuY SUz6NwVsJouAqsT1kzvYQGxeARuJY4v3MkLskpdYveEA2ExOAVuJ+78rQcJCQCVXT7xgncDI t4CRYRWjZGpBcW56brFhgWFearlecWJucWleul5yfu4mRnDwamnuYLy8JP4QowAHoxIPb4bG 1Vgh1sSy4srcQ4wSHMxKIrx7rl+JFeJNSaysSi3Kjy8qzUktPsQozcGiJM77NO9YpJBAemJJ anZqakFqEUyWiYNTqoHRbb1z06so4b+HNv5O6Q3nnbTvxZys9OsTJxRvtp2v3uxkH/T3/MR3 lgz8i0wsD31nKXRo1bO0faMxf+OOWbHRjdKNxxJXrlbRnPdFkvPt7YrCxCmVJRqqyvOWPHzw Z5qQapHIoWUrBPNnnXD0d3v54NEtl4BHVQePrpr7ISikRLKzxoFj+ocPSizFGYmGWsxFxYkA 9C6WnVoCAAA= X-CMS-MailID: 20191121052918epcas1p31b6aa77d4642ea390e8390bc782d590e X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191121052918epcas1p31b6aa77d4642ea390e8390bc782d590e References: <20191121052618.31117-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..6a8d88db4a0e --- /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_sb_info *sbi = EXFAT_SB(sb); + struct buffer_head *bh; + + exfat_chain_set(&clu, sbi->root_dir, 0, ALLOC_FAT_CHAIN); + + while (clu.dir != EOF_CLUSTER) { + for (i = 0; i < sbi->dentries_per_clu; i++) { + struct exfat_dentry *ep; + + 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 Thu Nov 21 05:26:13 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11255337 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 C4BBC14C0 for ; Thu, 21 Nov 2019 05:30:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 9B0622089F for ; Thu, 21 Nov 2019 05:30:06 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="MUGaGKUQ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726658AbfKUF30 (ORCPT ); Thu, 21 Nov 2019 00:29:26 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:58253 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726437AbfKUF3Z (ORCPT ); Thu, 21 Nov 2019 00:29:25 -0500 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20191121052921epoutp048e8872c289b0de085bfa31cf7ffcb9f2~ZFmOPtrBy2382823828epoutp04d for ; Thu, 21 Nov 2019 05:29:21 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20191121052921epoutp048e8872c289b0de085bfa31cf7ffcb9f2~ZFmOPtrBy2382823828epoutp04d DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574314161; bh=KfGbXTFk2S+92ux8sJu+T23qMAykMWjoPWvAZ6jNIy8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=MUGaGKUQv6WXFuq1qPUO9f/VG3+zXxxgdiMXX5FetKV0jhbYvxL2Q/b70gh+w6S9H rWLqcUcNrbq2XBvBKM/Ajv7xRXUerOW8n+FXFztoPtOIy68adcEnFZ7CKKwRgoxEgj yewGEiRCmPJE2ZZo8jWBLDGvMWZSILorxOfXRKfA= Received: from epsnrtp4.localdomain (unknown [182.195.42.165]) by epcas1p4.samsung.com (KnoxPortal) with ESMTP id 20191121052921epcas1p4d411e712fa74f498f8171f5a4053109b~ZFmNvyyjN2184121841epcas1p4B; Thu, 21 Nov 2019 05:29:21 +0000 (GMT) Received: from epsmges1p4.samsung.com (unknown [182.195.40.166]) by epsnrtp4.localdomain (Postfix) with ESMTP id 47JSmH6QtvzMqYkr; Thu, 21 Nov 2019 05:29:19 +0000 (GMT) Received: from epcas1p1.samsung.com ( [182.195.41.45]) by epsmges1p4.samsung.com (Symantec Messaging Gateway) with SMTP id 08.10.04406.FA026DD5; Thu, 21 Nov 2019 14:29:19 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p1.samsung.com (KnoxPortal) with ESMTPA id 20191121052919epcas1p1de897233478ce6428df75176611cc338~ZFmMJKdRX0315303153epcas1p1T; Thu, 21 Nov 2019 05:29:19 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191121052919epsmtrp114d91f27205b3d8be373b11a41094c89~ZFmMIXeZN1320713207epsmtrp15; Thu, 21 Nov 2019 05:29:19 +0000 (GMT) X-AuditID: b6c32a38-947ff70000001136-e2-5dd620af08fe Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id F6.06.03654.FA026DD5; Thu, 21 Nov 2019 14:29:19 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191121052919epsmtip104c696e4bc6701a93e1c5390a46978af~ZFmL_PAmm1025410254epsmtip1i; Thu, 21 Nov 2019 05:29:19 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, nborisov@suse.com, Namjae Jeon Subject: [PATCH v4 08/13] exfat: add exfat cache Date: Thu, 21 Nov 2019 00:26:13 -0500 Message-Id: <20191121052618.31117-9-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191121052618.31117-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFuphk+LIzCtJLcpLzFFi42LZdlhTV3e9wrVYg2vvlSwOP57EbtG8eD2b xcrVR5ksrt+9xWyxZ+9JFovLu+awWfyf9ZzV4sf0eov/b1pYLLb8O8Jqcen9BxYHbo+ds+6y e+yfu4bdY/fNBjaPvi2rGD3Wb7nK4rH5dLXH501yHoe2v2HzuP1sG0sAZ1SOTUZqYkpqkUJq XnJ+SmZeuq2Sd3C8c7ypmYGhrqGlhbmSQl5ibqqtkotPgK5bZg7QsUoKZYk5pUChgMTiYiV9 O5ui/NKSVIWM/OISW6XUgpScAkODAr3ixNzi0rx0veT8XCtDAwMjU6DKhJyMFeuSC3rcKpq+ zWJqYNxo0cXIySEhYCKxZVMzaxcjF4eQwA5GiQ37TjFCOJ8YJZY074fKfGOUuDntCBNMy96T GxhBbCGBvYwSrV0IHe8nPGfrYuTgYBPQlvizRRSkRkTAXmLz7AMsIDXMAtcZJZ7dmsEKkhAW MJD43LqaBcRmEVCVONG8nxnE5hWwkXh79ybUMnmJ1RsOMIPM5BSwlbj/uxJkjoTAbTaJ9sfb wOISAi4Slw4xQpQLS7w6voUdwpaSeNnfxg5RUi3xEWK6hEAHo8SL77YQtrHEzfUbWEFKmAU0 Jdbv0ocIK0rs/D0XbCKzAJ/Eu689rBBTeCU62oQgSlQl+i4dhrpRWqKr/QPUUg+JA6vOs0EC ZAKjxKwbb1gmMMrNQtiwgJFxFaNYakFxbnpqsWGBCXJsbWIEp0Qtix2Me875HGIU4GBU4uHN 0LgaK8SaWFZcmXuIUYKDWUmEd8/1K7FCvCmJlVWpRfnxRaU5qcWHGE2BwTiRWUo0OR+YrvNK 4g1NjYyNjS1MzMzNTI2VxHk5flyMFRJITyxJzU5NLUgtgulj4uCUamA8nvko/G9Bf2qHIc8U tQXbJdi1zMSCLrmcrv+tYcb/UvGI3qnNrAG6DqFvwq3Y6kT0hSadEPB4sy69lP3CW1mGxZUN x3uNGjq73ffM/Fn5jFPA+3jXVr3EXcfsoj6bpzJKMxYsCbz/VXTThB9Kx1veKe/ofHr+i+eh baH/Yi3v+mR5Gkkl2SmxFGckGmoxFxUnAgAaofgznwMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrOLMWRmVeSWpSXmKPExsWy7bCSnO56hWuxBk2rBS0OP57EbtG8eD2b xcrVR5ksrt+9xWyxZ+9JFovLu+awWfyf9ZzV4sf0eov/b1pYLLb8O8Jqcen9BxYHbo+ds+6y e+yfu4bdY/fNBjaPvi2rGD3Wb7nK4rH5dLXH501yHoe2v2HzuP1sG0sAZxSXTUpqTmZZapG+ XQJXxop1yQU9bhVN32YxNTButOhi5OSQEDCR2HtyA2MXIxeHkMBuRolfs3YzQSSkJY6dOMPc xcgBZAtLHD5cDFHzgVGid0EDWJxNQFvizxZRkHIRAUeJ3l2HWUBqmAUeM0qcOP+EESQhLGAg 8bl1NQuIzSKgKnGieT8ziM0rYCPx9u5NqF3yEqs3HACbySlgK3H/dyVIWAio5OqJF6wTGPkW MDKsYpRMLSjOTc8tNiwwzEst1ytOzC0uzUvXS87P3cQIDl4tzR2Ml5fEH2IU4GBU4uHN0Lga K8SaWFZcmXuIUYKDWUmEd8/1K7FCvCmJlVWpRfnxRaU5qcWHGKU5WJTEeZ/mHYsUEkhPLEnN Tk0tSC2CyTJxcEo1MMbuVFgarM9wtOOFcpnQwYe2R89f+sV7f8aX8EibU0szNU9df2u0Z9el pa2sc+caSB5kSw440ywWFBPvtvZMiXjTYbc3d358flT0cJISW8GM/bzz35x/+ObM+8V39P6m 2XXqbz6hdnnnudnf1Ur9be+yeP7SnOxjobnpLZ//pMNM19fcK1Fz89imxFKckWioxVxUnAgA d+6lLloCAAA= X-CMS-MailID: 20191121052919epcas1p1de897233478ce6428df75176611cc338 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191121052919epcas1p1de897233478ce6428df75176611cc338 References: <20191121052618.31117-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..74f9a44471d5 --- /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 unlock; /* 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); +unlock: + 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 Thu Nov 21 05:26:14 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11255321 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 6A1BC138C for ; Thu, 21 Nov 2019 05:29:27 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 389012089F for ; Thu, 21 Nov 2019 05:29:27 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="iD/2o8k4" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726620AbfKUF3Z (ORCPT ); Thu, 21 Nov 2019 00:29:25 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:58267 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726546AbfKUF3Y (ORCPT ); Thu, 21 Nov 2019 00:29:24 -0500 Received: from epcas1p2.samsung.com (unknown [182.195.41.46]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20191121052922epoutp041688764f1d2ae1e4ff059c0eee0ea324~ZFmOr1YcM2300223002epoutp04S for ; Thu, 21 Nov 2019 05:29:22 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20191121052922epoutp041688764f1d2ae1e4ff059c0eee0ea324~ZFmOr1YcM2300223002epoutp04S DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574314162; bh=6pP9/t3Y+QPUL8I3w8RVSR4AP1ZnhZ/R45fIUNg9Vvs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=iD/2o8k4SRDSgA2eKsmn3Fqcnb3TIOvzs7ml9xmSZ1ywXffK9ja6qDGJuvECCWmBv TqjRbEMLOup+eyoMTcE1JGxEk+kxQgGNYImQ6WS2lyOgNAyNYqO3tj484q4x71gjl4 4dgPVIJpfyy2ysXFqP8/slnxKAKjXeI2JlyS9GB4= Received: from epsnrtp3.localdomain (unknown [182.195.42.164]) by epcas1p1.samsung.com (KnoxPortal) with ESMTP id 20191121052921epcas1p1e8d84936bb4893709fd41bacca5a9720~ZFmOCJ1Tx0315303153epcas1p1e; Thu, 21 Nov 2019 05:29:21 +0000 (GMT) Received: from epsmges1p1.samsung.com (unknown [182.195.40.162]) by epsnrtp3.localdomain (Postfix) with ESMTP id 47JSmJ2zhMzMqYkl; Thu, 21 Nov 2019 05:29:20 +0000 (GMT) Received: from epcas1p2.samsung.com ( [182.195.41.46]) by epsmges1p1.samsung.com (Symantec Messaging Gateway) with SMTP id C3.9C.04072.0B026DD5; Thu, 21 Nov 2019 14:29:20 +0900 (KST) Received: from epsmtrp2.samsung.com (unknown [182.195.40.14]) by epcas1p2.samsung.com (KnoxPortal) with ESMTPA id 20191121052920epcas1p2a38edd92dece3ad2cec74439175fcc52~ZFmMw9q4L0047500475epcas1p2e; Thu, 21 Nov 2019 05:29:20 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp2.samsung.com (KnoxPortal) with ESMTP id 20191121052920epsmtrp25a607e14b549ba8b72183586db86b0fa~ZFmMwPnka1671516715epsmtrp2K; Thu, 21 Nov 2019 05:29:20 +0000 (GMT) X-AuditID: b6c32a35-9a5ff70000000fe8-b8-5dd620b0c246 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 08.06.03654.FA026DD5; Thu, 21 Nov 2019 14:29:19 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191121052919epsmtip1f6bcca9a0a7677560e4a21a210428b40~ZFmMjHWOq1311613116epsmtip1a; Thu, 21 Nov 2019 05:29:19 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, nborisov@suse.com, Namjae Jeon Subject: [PATCH v4 09/13] exfat: add misc operations Date: Thu, 21 Nov 2019 00:26:14 -0500 Message-Id: <20191121052618.31117-10-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191121052618.31117-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA01Sa0hTYRjm27nsaE1O89KHRa0TQlbajmt6ilZBtwNFGUFBIXZwJyeena2d TTKpRobVKFFLjKURdMEy0ua6aN5QK8Sw1EyWqWiQEeKti1GZbR67/Hve532e9314v49A1Ll4 JJEm2nmbyAkUHow+aIqOjanQvE7S1rSEMk3vCpRM9rVynLlV9kTBdPe+QZia2haU6awuxplp 9xDGfCs6wUwPn0IZ769mjOkYHUM3zmGr3L1Ktr7kjpJ97HPibK73NmDLvV0oW9maxX7yLGIb Hw7jbM/7B2hi0H5hnYnnjLxNw4spFmOamGqgtu9J3pSsj9fSMfQaJoHSiJyZN1CbdyTGbE0T /GEpTQYnOPxUIidJ1Kr162wWh53XmCyS3UDxVqNgpbXWWIkzSw4xNTbFYl5La7Vxer/yoGBq u+/BrL1rjoy0t+BO0LnSBYIISK6GPaXtwAWCCTX5CMAbbYMKuZgAsKQoG5WLrwCOnTyP/LFk TzhnG7UA/pzKAX8thZ4upQsQBE6ugD+94QFDGLkBVl5umDEgZDeA799cwgKNUP+kuzfb0ABG ySj4ua4YBLCKNMCr58cV8rbFsKyiAQnMDPLz/T8yA3Mg2YPDAt/bGR6Sm2F+9iFZHgo/PvMq ZRwJP43U4rIkC47Xz+Y/A+CHSYOMddBXXoEFJAgZDcurV8n0Elj1o2QmDEKGwJEv5zB5igqe yVHLkiiY29E0m3EBdJ0em13KQvdACS4fJA/A1uoGJA8scv/bcBWA2yCCt0rmVF6irfT/7+UB M39xuf4RuNi2oxGQBKDmqkzLupLUGJchZZobASQQKkxV0/0qSa0ycplHeZsl2eYQeKkR6P1n zEciw1Ms/p8t2pNpfZxOp2NWxyfE63XUfBXxrT1JTaZydj6d56287Y9PQQRFOsEFQz3hEx4f aI8YUk/iBzrsT7fV5TdvKe37Or2SHBo4VFmZdviJI2VnxKDCIwxGH391a+LOaF+CMmdpTYYb E7P6e8t2L7TsKxzdle7qYDOVziLs+dS9KpXa8Wxs+7Ej+zbRL28UzAk553kx72z+9e+qD77J BXUjU3F7jeKVVtrZT6GSiaOXIzaJ+w2Px28yoQMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrBLMWRmVeSWpSXmKPExsWy7bCSnO56hWuxBg33zCwOP57EbtG8eD2b xcrVR5ksrt+9xWyxZ+9JFovLu+awWfyf9ZzV4sf0eov/b1pYLLb8O8Jqcen9BxYHbo+ds+6y e+yfu4bdY/fNBjaPvi2rGD3Wb7nK4rH5dLXH501yHoe2v2HzuP1sG0sAZxSXTUpqTmZZapG+ XQJXxrmtm1gL7lpWvLt4kq2B8bJOFyMnh4SAiUTzpwaWLkYuDiGB3YwS387dYYdISEscO3GG uYuRA8gWljh8uBii5gOjxJ1rv9hB4mwC2hJ/toiClIsIOEr07joMNodZ4DGjxInzTxhBEsJA C9YtO8cCYrMIqEp82TcHLM4rYCuxoPcjE8QueYnVGw6A7eIEit//XQkSFhKwkbh64gXrBEa+ BYwMqxglUwuKc9Nziw0LDPNSy/WKE3OLS/PS9ZLzczcxgsNXS3MH4+Ul8YcYBTgYlXh4MzSu xgqxJpYVV+YeYpTgYFYS4d1z/UqsEG9KYmVValF+fFFpTmrxIUZpDhYlcd6neccihQTSE0tS s1NTC1KLYLJMHJxSDYxlN/cHquU1fp9r1CohwOle5bPMMjLFQGTytC/CxrNMWoxmrs7S1VLP La58qn9pi+HSb1VZdztdS1QO730huZ7lxc9bffn3s65esJ6Y/tF+Yby9m3fjrvopB79xBXmU NZ1jTlOf/XcP94PTKStmnCr5+y/Rd4nlQj9HNfazQhFGyY/bbic1myixFGckGmoxFxUnAgAb C6YtWwIAAA== X-CMS-MailID: 20191121052920epcas1p2a38edd92dece3ad2cec74439175fcc52 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191121052920epcas1p2a38edd92dece3ad2cec74439175fcc52 References: <20191121052618.31117-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..a3b35ed4db05 --- /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) +{ + set_bit(EXFAT_SB_DIRTY, &EXFAT_SB(sb)->s_state); + 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 Thu Nov 21 05:26:15 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11255331 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 78FF1138C for ; Thu, 21 Nov 2019 05:30:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 32C5F20898 for ; Thu, 21 Nov 2019 05:30:03 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="Ywmn7AT8" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727046AbfKUF3z (ORCPT ); Thu, 21 Nov 2019 00:29:55 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:58277 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725904AbfKUF31 (ORCPT ); Thu, 21 Nov 2019 00:29:27 -0500 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20191121052922epoutp04aefbebbbb26755d5aa2490f07cc1ad31~ZFmPHGP4S2300223002epoutp04T for ; Thu, 21 Nov 2019 05:29:22 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20191121052922epoutp04aefbebbbb26755d5aa2490f07cc1ad31~ZFmPHGP4S2300223002epoutp04T DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574314162; bh=1N7hsS76/XME+2yy5BOUoNc9qioDqX528NcPy8mT13o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Ywmn7AT8qUjF891H7l6AR0LsDNA7FgYVvdFKJSM6FqKuRg9NhvpWpFw27Tmr/7ry+ GSSCqYoAqhxQpJxQSO4Qxy/x1T8EiKW/+ItPfbpxzSEpT4oblBICayfFH+1KY2jYXX DXNIoABYoDkwXMbNX55SpuPBfVI9Fc/6j6s0Utos= Received: from epsnrtp2.localdomain (unknown [182.195.42.163]) by epcas1p4.samsung.com (KnoxPortal) with ESMTP id 20191121052922epcas1p4bd5ad46c79468edf29183030cf062b06~ZFmOnDTWL2184121841epcas1p4H; Thu, 21 Nov 2019 05:29:22 +0000 (GMT) Received: from epsmges1p2.samsung.com (unknown [182.195.40.162]) by epsnrtp2.localdomain (Postfix) with ESMTP id 47JSmJ6yHxzMqYkh; Thu, 21 Nov 2019 05:29:20 +0000 (GMT) Received: from epcas1p3.samsung.com ( [182.195.41.47]) by epsmges1p2.samsung.com (Symantec Messaging Gateway) with SMTP id 8C.E7.04235.0B026DD5; Thu, 21 Nov 2019 14:29:20 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p3.samsung.com (KnoxPortal) with ESMTPA id 20191121052920epcas1p3e5b6c0251e869e265d19798dbeebab4e~ZFmNU-avP0226102261epcas1p3d; Thu, 21 Nov 2019 05:29:20 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191121052920epsmtrp1dbb5e51ba96bd417f8b74bdd35d3bf9e~ZFmNURKYX1320813208epsmtrp15; Thu, 21 Nov 2019 05:29:20 +0000 (GMT) X-AuditID: b6c32a36-e07ff7000000108b-36-5dd620b05ef9 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 29.06.03654.0B026DD5; Thu, 21 Nov 2019 14:29:20 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191121052920epsmtip1e82b3b14894e885eb3c63e93ce6e6299~ZFmNH5KW40971209712epsmtip1g; Thu, 21 Nov 2019 05:29:20 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, nborisov@suse.com, Namjae Jeon Subject: [PATCH v4 10/13] exfat: add nls operations Date: Thu, 21 Nov 2019 00:26:15 -0500 Message-Id: <20191121052618.31117-11-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191121052618.31117-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA01Sa0hTYRju27nszJoeltnHAlsHojTNzTk7hesqdSIJKSoSlx3myUlnF3am ZRIJhcqqNbtgakIXCEpF0WFeSWZ2J9C8YGEru5msm10WGdbm6fbveZ/ved7n5f1eAlG4cSWR a3FwdgvLU3gY2twdkxDfoBo0qKuqVtDdz09K6cOX6nH6Sk2PhB4aeYTQHZ13UPph2zmc/ln5 GqO/lR+if/qPoLRn6gZG973/gK6eybRWjkiZ69W1UqZ9uAhnXJ6rgKn3DKBM071C5lNjNOO9 5seZx6+a0XRZBp9i4thszq7iLEZrdq4lR09t2pq1LkuXrNbEa5bTyyiVhTVzeio1LT1+fS4f HJZS5bN8XpBKZwWBSliZYrfmOTiVySo49BRny+ZtGrVtqcCahTxLzlKj1bxCo1Yn6oLK3bzp gfsuZntRIdl/s70RFIHxN8AJZAQkk+BAwCV1gjBCQbYA2Nt2HxeLCQDrSickIZWC/ArguNf4 x+G5dBkTRZ0A3jnaj/11fO+6FywIAieXwB+eOSFDJLkKNlV1oSENQg4B+OrRWSz0MJvUQm/H KBLCKLkQlhz9PJ0mJ/XwaUU/KqbNhzUNXUiopyzI+yYLQn0g6cPhcXcLJmpSoXvCiYt4Nhy/ 5ZGKWAnfnCiWhryQLIQfryMiXQrgWEAvYi0crm+YHhkhY2B9W4JIL4Ctk9XTG0LIcPjuyzFM 7CKHpcUKUbIQuvq6JSKeB50lH36HMrD3zODvhboBrC0uBW4QXfkv4TwAV0EUZxPMOZygsSX+ /2ONYPoaY5NbwMUHaV5AEoCaJTctHjAoMDZfKDB7ASQQKlLeMdRvUMiz2YIDnN2aZc/jOcEL dME9liHKOUZr8LYtjiyNLlGr1dJJycuSdVpqrpz41mtQkDmsg9vLcTbO/scnIWTKItDDcH58 KhBWQidp89v7egKHN265HZER51scQYxtrtgXwLZlhFfLasrXtD87uwHfPuN2ybkdUyOTPuM8 5+fYwrjaUR9zgd+UcCpqzBNvOJTmKssMFBl2VdhT3mUOmBv4l6dnbN2GuU5UPVmUGaGvSVNG 3tjrf3uwKXrn2ow6/577FCqYWE0sYhfYXypyMLmjAwAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrJLMWRmVeSWpSXmKPExsWy7bCSnO4GhWuxBj8XKlgcfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9Rb/37SwWGz5d4TV4tL7DywO3B47Z91l 99g/dw27x+6bDWwefVtWMXqs33KVxWPz6WqPz5vkPA5tf8PmcfvZNpYAzigum5TUnMyy1CJ9 uwSujHMTTrEWPJnJVHFs9ybGBsZXLxm7GDk5JARMJLYsXsbaxcjFISSwm1Fi1+F7rBAJaYlj J84wdzFyANnCEocPF4OEhQQ+MEqc+aAJEmYT0Jb4s0UUJCwi4CjRu+swC8gYZoHHjBInzj8B my8sYCxxaM8jZhCbRUBVor37CxOIzStgK/Fg5hUWiFXyEqs3HABbxQkUv/+7EmKVjcTVEy9Y JzDyLWBkWMUomVpQnJueW2xYYJiXWq5XnJhbXJqXrpecn7uJERzAWpo7GC8viT/EKMDBqMTD m6FxNVaINbGsuDL3EKMEB7OSCO+e61dihXhTEiurUovy44tKc1KLDzFKc7AoifM+zTsWKSSQ nliSmp2aWpBaBJNl4uCUamDkuZbOs79AXOffBl/JZT2hUlmsol73p/Hq9esI29+vyNfck3PN 4vv1yCezjMpPnk7WtKvmfa043+6Ku3rtDIsjJvv3MWfavkj2un/kVN1uh4+Mj94viHwi7nKa x/a+ZsSEEnefkr+Zq654hNo3m2eLl8cmCDByM92KePL2crb83qNNBxYodyixFGckGmoxFxUn AgBfrBp4XAIAAA== X-CMS-MailID: 20191121052920epcas1p3e5b6c0251e869e265d19798dbeebab4e X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191121052920epcas1p3e5b6c0251e869e265d19798dbeebab4e References: <20191121052618.31117-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 | 809 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 809 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..c944ef7167be --- /dev/null +++ b/fs/exfat/nls.c @@ -0,0 +1,809 @@ +// 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 release_bh; + } + 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) { + 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; +release_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 = ALLOC_FAT_CHAIN; + + 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 Thu Nov 21 05:26:16 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11255339 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 19D86138C for ; Thu, 21 Nov 2019 05:30:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id EC30D20898 for ; Thu, 21 Nov 2019 05:30:07 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="hEFZ0Otk" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727114AbfKUFaH (ORCPT ); Thu, 21 Nov 2019 00:30:07 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:58284 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726593AbfKUF3Y (ORCPT ); Thu, 21 Nov 2019 00:29:24 -0500 Received: from epcas1p3.samsung.com (unknown [182.195.41.47]) by mailout4.samsung.com (KnoxPortal) with ESMTP id 20191121052922epoutp04de48d8bd498191a1ffd95c5860ebc048~ZFmPibqp42288822888epoutp04h for ; Thu, 21 Nov 2019 05:29:22 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout4.samsung.com 20191121052922epoutp04de48d8bd498191a1ffd95c5860ebc048~ZFmPibqp42288822888epoutp04h DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574314163; bh=W/nvY8vh7b5VKEGm7/5P+NKNPdIjQbHkTzk7gijW0ro=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=hEFZ0Otk8o8UQy5dNrFejXModNYn4TniglgN+PusistERayyB44cxvS4+XAGol9tv 6KuSjUAFdDrzIKcoCZkDYrBH5OZq5oIAZCGhXabf/lkyXNgJlRayGk0FTPIIvEAHMG dIfJ2ckRdvB5nAdPsXru+3otONyNMr24ox8j34OY= Received: from epsnrtp2.localdomain (unknown [182.195.42.163]) by epcas1p4.samsung.com (KnoxPortal) with ESMTP id 20191121052922epcas1p485af334a8f0c165bafb8369d13e8392c~ZFmPE7R9o2184121841epcas1p4J; Thu, 21 Nov 2019 05:29:22 +0000 (GMT) Received: from epsmges1p4.samsung.com (unknown [182.195.40.162]) by epsnrtp2.localdomain (Postfix) with ESMTP id 47JSmK46x7zMqYkV; Thu, 21 Nov 2019 05:29:21 +0000 (GMT) Received: from epcas1p4.samsung.com ( [182.195.41.48]) by epsmges1p4.samsung.com (Symantec Messaging Gateway) with SMTP id D9.10.04406.1B026DD5; Thu, 21 Nov 2019 14:29:21 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p3.samsung.com (KnoxPortal) with ESMTPA id 20191121052921epcas1p35c720135e315a34d16800057e8e67829~ZFmNy0YZv1390713907epcas1p3R; Thu, 21 Nov 2019 05:29:21 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191121052921epsmtrp11e8aac9a15108ec63c0d0beabfb68586~ZFmNyEsKT1320713207epsmtrp1_; Thu, 21 Nov 2019 05:29:21 +0000 (GMT) X-AuditID: b6c32a38-95fff70000001136-eb-5dd620b10c3e Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 3A.06.03654.1B026DD5; Thu, 21 Nov 2019 14:29:21 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191121052920epsmtip12a6985b8fe1b27aaeee1a4b55153ef0e~ZFmNo47SW1143211432epsmtip1w; Thu, 21 Nov 2019 05:29:20 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, nborisov@suse.com, Namjae Jeon Subject: [PATCH v4 11/13] exfat: add Kconfig and Makefile Date: Thu, 21 Nov 2019 00:26:16 -0500 Message-Id: <20191121052618.31117-12-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191121052618.31117-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA02SWUwTURSGvZ3pdIpWJ1XkpkStE4mCFjqUwrhUTTQ4riH4pKbBESYU7ZZO iyIPorggKlYTEVEiUeOuhVKRyqKBGlSCiVUg1BAVAy4hghi3qGiHcXv7z3+/s+TcgyPKg5gK z7E6OYeVNZNYBFrbEqvVVKs7jdp9hVPolpdHZXThWQ9GX7pyV0J39YQQuqHxPko/vnUKo3+W v5LSX47voH8O7EZp30hASgcHh9DFYxl/eY+MuV1xVcbUdxdgTInvMmA8vg6UqWnLZz54pzLN Nwcw5ml/LZomX29eYOLYLM6h5qyZtqwca7aBXLk2Y0mGPllLaai5dAqptrIWzkAuXZWmSc0x h4cl1bms2RW20lieJxMWLnDYXE5ObbLxTgPJ2bPMdkprj+dZC++yZsdn2izzKK02UR8mN5pN gYHl9iLFNvf3QrQABCOKgRyHRBL0jpTJikEEriTqABwK9SNiMAxgfd8HiRh8AvBC9zm0GOCj KUXtKtFvBPCqf6fkb8b1vucSAcKI2fC7L1JoMYlYBGtO3kEFBiG6AOwPlUmFh4nEXNja9wAV NErEQLfn3qivIAxwT5UfFeebBq9U3UGEmvKw/+xbnlAHEk8xeL5yQCYOtBSW7mJFfCJ82+qT iVoF3xze+xvJh+9vI6JdBODrzwZR62C3p0oqIAgRCz23EkR7OvR/qwCCRojx8N3Hg1KxigIW 7VWKSAwsCbZIRB0Ni/cN/W7KwM7SakxciBvApo6dMjeYWv6vQyUAl8Fkzs5bsjmesif9/1te MHqJcXQdaHi4qhkQOCDHKUyzOoxKKZvL51maAcQRcpKioeuJUanIYvO2cw5bhsNl5vhmoA+v 8Qiiisy0he/a6syg9Ik6nY5OSk5J1uvIKAX+5ZFRSWSzTm4Lx9k5x588CS5XFQB0zJqtc86s +Do/1LlO1/2CS02/vj9dih39kTIrqoFaOTihQnOvKT1u5nBCZWN9aNPhoKapNxqvrstnHwYP 3fTELDuQu/pj392y+Wkd4+M3z5ux/VDsk5GmR4HNF9taoObEuV5Jf0F79NA1VwmZLHcQsa1e 1Wn3hsmJN/KOUYEIdYhEeRNLxSEOnv0FQESCXJ8DAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrGLMWRmVeSWpSXmKPExsWy7bCSnO5GhWuxBh+vMVscfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9Rb/37SwWGz5d4TV4tL7DywO3B47Z91l 99g/dw27x+6bDWwefVtWMXqs33KVxWPz6WqPz5vkPA5tf8PmcfvZNpYAzigum5TUnMyy1CJ9 uwSujCNvPAs6eCsm/GlmaWC8xNXFyMEhIWAi0XFWqouRi0NIYDejxPX/i1i6GDmB4tISx06c YYaoEZY4fLgYouYDo0TvxAesIHE2AW2JP1tEQcpFBBwlencdZgGpYRZ4zChx4vwTRpCEsICl xPGnp8BmsgioSkxYf4IVxOYVsJVo3bATape8xOoNB8B2cQLF7/+uBAkLCdhIXD3xgnUCI98C RoZVjJKpBcW56bnFhgWGeanlesWJucWleel6yfm5mxjBoauluYPx8pL4Q4wCHIxKPLwZGldj hVgTy4orcw8xSnAwK4nw7rl+JVaINyWxsiq1KD++qDQntfgQozQHi5I479O8Y5FCAumJJanZ qakFqUUwWSYOTqkGxvlODAJmLx9Is5ZsDLrisPRmfknamaJ92mu+z5FUmyUX0Xl7x9RTn9xe dChfnflw148FmkUPlF5//LTm1MaOnYnusY9FBQ5Whcau9Si9dlPl/eGfrrM+1O8+9WTCzOsx E9+4FBmYsc9jidKcEWe5sGnXkvOzDKe7rphUH9fEW66lVZ8l9rD2iLESS3FGoqEWc1FxIgAL 2Qd3WQIAAA== X-CMS-MailID: 20191121052921epcas1p35c720135e315a34d16800057e8e67829 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191121052921epcas1p35c720135e315a34d16800057e8e67829 References: <20191121052618.31117-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 Thu Nov 21 05:26:17 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11255325 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 331A6109A for ; Thu, 21 Nov 2019 05:29:38 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 13C282089D for ; Thu, 21 Nov 2019 05:29:38 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="QCvGprBT" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726792AbfKUF33 (ORCPT ); Thu, 21 Nov 2019 00:29:29 -0500 Received: from mailout1.samsung.com ([203.254.224.24]:62551 "EHLO mailout1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726705AbfKUF33 (ORCPT ); Thu, 21 Nov 2019 00:29:29 -0500 Received: from epcas1p2.samsung.com (unknown [182.195.41.46]) by mailout1.samsung.com (KnoxPortal) with ESMTP id 20191121052923epoutp0104aa1868708839fbfdf3a9b1b506867d~ZFmQSUVfE1285312853epoutp01J for ; Thu, 21 Nov 2019 05:29:23 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.samsung.com 20191121052923epoutp0104aa1868708839fbfdf3a9b1b506867d~ZFmQSUVfE1285312853epoutp01J DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574314163; bh=LzsUvoRQ+/EvYb4sCIgQeQ8VbnU5fmhs4hyN/5tdGrs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=QCvGprBT1OhYCMuwEBMqw3mT4L/5pfvYzWK0jkmGsrADMq4k5edLaiCjvEZ2LHGJR 9WWe/FKtAixjn3xyrzgxh/XSd+SreYqDkhcCapsXUzvTXVAEyKevFz67TZpUHv5KYz 19xqNPKukDRjE/uX57zdCEHcUKnPLD91bNNHjqGU= Received: from epsnrtp2.localdomain (unknown [182.195.42.163]) by epcas1p2.samsung.com (KnoxPortal) with ESMTP id 20191121052923epcas1p2db841f8c65bf88c587f95a5d2e34913b~ZFmPzJTFA3070730707epcas1p2s; Thu, 21 Nov 2019 05:29:23 +0000 (GMT) Received: from epsmges1p1.samsung.com (unknown [182.195.40.162]) by epsnrtp2.localdomain (Postfix) with ESMTP id 47JSmL2Y4RzMqYkh; Thu, 21 Nov 2019 05:29:22 +0000 (GMT) Received: from epcas1p2.samsung.com ( [182.195.41.46]) by epsmges1p1.samsung.com (Symantec Messaging Gateway) with SMTP id 3A.9C.04072.2B026DD5; Thu, 21 Nov 2019 14:29:22 +0900 (KST) Received: from epsmtrp2.samsung.com (unknown [182.195.40.14]) by epcas1p1.samsung.com (KnoxPortal) with ESMTPA id 20191121052921epcas1p1f3a18589dce59939af6b462f35412642~ZFmOhmscp0315303153epcas1p1g; Thu, 21 Nov 2019 05:29:21 +0000 (GMT) Received: from epsmgms1p2new.samsung.com (unknown [182.195.42.42]) by epsmtrp2.samsung.com (KnoxPortal) with ESMTP id 20191121052921epsmtrp23866cba3e7cff6caba222d783180f6ab~ZFmOcMb-s1671516715epsmtrp2O; Thu, 21 Nov 2019 05:29:21 +0000 (GMT) X-AuditID: b6c32a35-9a5ff70000000fe8-be-5dd620b2738f Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p2new.samsung.com (Symantec Messaging Gateway) with SMTP id 63.98.03814.1B026DD5; Thu, 21 Nov 2019 14:29:21 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191121052921epsmtip15b70f610a8ab1e7a2f1f7f33092767e7~ZFmOQUcD61142911429epsmtip17; Thu, 21 Nov 2019 05:29:21 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, nborisov@suse.com, Namjae Jeon Subject: [PATCH v4 12/13] exfat: add exfat in fs/Kconfig and fs/Makefile Date: Thu, 21 Nov 2019 00:26:17 -0500 Message-Id: <20191121052618.31117-13-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191121052618.31117-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA01Sa0zTUBj1rl1X0JEypl4x0VkligZYGZtVmZpITBUSCfwzEiis2Yh7NO1m nPwQ4zNIVHwA4hMRVJAMYfJQ8AG+TVQQJaBEDUaNGkFRQSXqSvHx73znnnPPd7/74YgmHwvH s51uTnCydhILRuvbIqOjanWP0/TljVq6rW+vit5c5sPoM1XXFXRXbw9CN7fcRumHFw5j9K+S 10p6uGgj/ev9FpT2/7ympDv6B9Cl45mmkl4Vc/nIWRVzsTsXY3b5KwHj8z9Cmbq7Ocxg7TSm teE9xjx5VY8mB622x9s41sIJOs6Z5bJkO61mMjE1fVm60aSnoqgF9HxS52QdnJlMSEqOWp5t DzRL6taxdk+ASmZFkYxZHC+4PG5OZ3OJbjPJ8RY7T+n5aJF1iB6nNTrL5VhI6fWxxoAyw27b O1gO+Ap8/fXqBiQX9GN5IAiHRBw8UVQewMG4hmgE8FPtCCIXnwBs3lQ2VnwFsNr3JlDgo5aq fLPk1hAtAH4r1P41DN36qJI0GDEPjvgnShotsQTWHbqCShqE6ALwVU+xUjoIIxj4/flmIOlR IgKea7dItJoww6qhY6jc3XRYVXNlNDYowD/74ZWugcQTDJ6vKBh7QQK819CvkHEYfHvTr5Jx OBz80ILJLefAj5cRmd4B4Jshs4wNsNtXo5QkCBEJfRdiZHoGbPpxBEgYIULghy/5SvkWNdyx TSNLIuCujrax0Kkwb/vAWCgD7297CeSB7AHwZuFuxR4wreRfwnEAKsEkjhcdVk6keOr/36oF o5s419gI9t9LagUEDsgJatucR2kaJbtO9DpaAcQRUqtu7upM06gtrHcDJ7jSBY+dE1uBMTDG AiR8YpYrsNdOdzpljDUYDHScab7JaCAnq/Hh9jQNYWXd3FqO4znhj0+BB4XngoRZfa44K+VV 3VivjZgeWnRq7R3TlBWlr9VrVkQu/vx0QmpD6T7T7C/DM4WU3kj/ItPOA96nl+btK3gurOwf MIxL9Bpj6npiKT64aMhxOrkpI7vn6tZVxSG4Jycl6cBIfZmPz3sxBV+Y+ViR6U3qDH339UHq g/jPL0JPwoPGTt/RbhIVbSw1FxFE9jdrBZZQnwMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrKLMWRmVeSWpSXmKPExsWy7bCSnO5GhWuxBs/26FkcfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9Rb/37SwWGz5d4TV4tL7DywO3B47Z91l 99g/dw27x+6bDWwefVtWMXqs33KVxWPz6WqPz5vkPA5tf8PmcfvZNpYAzigum5TUnMyy1CJ9 uwSujEmflzIWLOOoOLp2O3MD43u2LkYODgkBE4nVPbZdjJwcQgK7GSUONeqC2BIC0hLHTpxh higRljh8uLiLkQuo5AOjxNuHk8HibALaEn+2iIKUiwg4SvTuOswCUsMs8JhR4sT5J4wgCWEB D4lfD5oZQepZBFQlNl5MAQnzCthKrP4+nwVilbzE6g0HwEZyAsXv/66EuMZG4uqJF6wTGPkW MDKsYpRMLSjOTc8tNiwwykst1ytOzC0uzUvXS87P3cQIDlwtrR2MJ07EH2IU4GBU4uHN0Lga K8SaWFZcmXuIUYKDWUmEd8/1K7FCvCmJlVWpRfnxRaU5qcWHGKU5WJTEeeXzj0UKCaQnlqRm p6YWpBbBZJk4OKUaGKf/F2cQfC79sezkwm+3zpzX2VbZ2Hr2Q2+sFIfnfFmBj+XXfUUT/IJ4 7vTN02x+ffhmB1f6sVX5U3c0OW47uPw6yy8v668Xlne1uuy/x8Ba8pBzekiqvmJP94wzx+sK 1r7cKnvlUvLri7oHNrnEN3pwNV5zknQpvOe5OdVYwul3etTZg5OXcCuxFGckGmoxFxUnAgAr 2SvgWAIAAA== X-CMS-MailID: 20191121052921epcas1p1f3a18589dce59939af6b462f35412642 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191121052921epcas1p1f3a18589dce59939af6b462f35412642 References: <20191121052618.31117-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 Thu Nov 21 05:26:18 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Namjae Jeon X-Patchwork-Id: 11255329 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 64F27138C for ; Thu, 21 Nov 2019 05:30:02 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 397532089F for ; Thu, 21 Nov 2019 05:30:02 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="M9Nl+8ob" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727051AbfKUF3z (ORCPT ); Thu, 21 Nov 2019 00:29:55 -0500 Received: from mailout2.samsung.com ([203.254.224.25]:30258 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726623AbfKUF31 (ORCPT ); Thu, 21 Nov 2019 00:29:27 -0500 Received: from epcas1p4.samsung.com (unknown [182.195.41.48]) by mailout2.samsung.com (KnoxPortal) with ESMTP id 20191121052924epoutp029539de862bfa0bd573b4fae23b5b502d~ZFmQ40Lsf0175801758epoutp02j for ; Thu, 21 Nov 2019 05:29:24 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.samsung.com 20191121052924epoutp029539de862bfa0bd573b4fae23b5b502d~ZFmQ40Lsf0175801758epoutp02j DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1574314164; bh=cebDS4wJnJkhFo4uYCXo0DH3otueb9YFlYeYEY3vVQE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=M9Nl+8obhK1RFwrGhcAg/qj1a95IuFTDCIEdnBlqAAa25tBIreH8NY8UnhMfp7jvK jL/WO57M4nTBNx0KhAobL8ROBCiZHleLKyZY9b6YyWVnleaRn7RSunq94q+q/bmoVt h0P3eEzySnxeMbiE95BPVDwLWI22A5Q0VScRi29M= Received: from epsnrtp4.localdomain (unknown [182.195.42.165]) by epcas1p2.samsung.com (KnoxPortal) with ESMTP id 20191121052923epcas1p2a8e607675c80b4ad3a73a050a9be6878~ZFmQMsPSJ0047900479epcas1p2c; Thu, 21 Nov 2019 05:29:23 +0000 (GMT) Received: from epsmges1p2.samsung.com (unknown [182.195.40.162]) by epsnrtp4.localdomain (Postfix) with ESMTP id 47JSmM0YthzMqYkj; Thu, 21 Nov 2019 05:29:23 +0000 (GMT) Received: from epcas1p3.samsung.com ( [182.195.41.47]) by epsmges1p2.samsung.com (Symantec Messaging Gateway) with SMTP id B1.F7.04235.2B026DD5; Thu, 21 Nov 2019 14:29:22 +0900 (KST) Received: from epsmtrp1.samsung.com (unknown [182.195.40.13]) by epcas1p4.samsung.com (KnoxPortal) with ESMTPA id 20191121052922epcas1p4c3957530e6d0ea89e1fc09322e999fb9~ZFmPGEO9N2154621546epcas1p4y; Thu, 21 Nov 2019 05:29:22 +0000 (GMT) Received: from epsmgms1p1new.samsung.com (unknown [182.195.42.41]) by epsmtrp1.samsung.com (KnoxPortal) with ESMTP id 20191121052922epsmtrp113cf68692eb44478fcc823d1928ef9d1~ZFmPCy12Y1320813208epsmtrp19; Thu, 21 Nov 2019 05:29:22 +0000 (GMT) X-AuditID: b6c32a36-defff7000000108b-3f-5dd620b25486 Received: from epsmtip1.samsung.com ( [182.195.34.30]) by epsmgms1p1new.samsung.com (Symantec Messaging Gateway) with SMTP id 8C.06.03654.2B026DD5; Thu, 21 Nov 2019 14:29:22 +0900 (KST) Received: from localhost.localdomain (unknown [10.88.103.87]) by epsmtip1.samsung.com (KnoxPortal) with ESMTPA id 20191121052922epsmtip157e89ab879a8f84b1fe10cc62b038b54~ZFmO2_ASG1142611426epsmtip15; Thu, 21 Nov 2019 05:29:22 +0000 (GMT) From: Namjae Jeon To: linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org Cc: gregkh@linuxfoundation.org, valdis.kletnieks@vt.edu, hch@lst.de, linkinjeon@gmail.com, Markus.Elfring@web.de, sj1557.seo@samsung.com, dwagner@suse.de, nborisov@suse.com, Namjae Jeon Subject: [PATCH v4 13/13] MAINTAINERS: add exfat filesystem Date: Thu, 21 Nov 2019 00:26:18 -0500 Message-Id: <20191121052618.31117-14-namjae.jeon@samsung.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20191121052618.31117-1-namjae.jeon@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFuplk+LIzCtJLcpLzFFi42LZdlhTX3eTwrVYgx07bCwOP57EbtG8eD2b xcrVR5ksrt+9xWyxZ+9JFovLu+awWfyf9ZzV4sf0eov/b1pYLLb8O8Jqcen9BxYHbo+ds+6y e+yfu4bdY/fNBjaPvi2rGD3Wb7nK4rH5dLXH501yHoe2v2HzuP1sG0sAZ1SOTUZqYkpqkUJq XnJ+SmZeuq2Sd3C8c7ypmYGhrqGlhbmSQl5ibqqtkotPgK5bZg7QsUoKZYk5pUChgMTiYiV9 O5ui/NKSVIWM/OISW6XUgpScAkODAr3ixNzi0rx0veT8XCtDAwMjU6DKhJyMpmW/mQq+sVa0 LTvB2MDYwNrFyMEhIWAisX+vZxcjJ4eQwA5GiVOPxLoYuYDsT4wS7xd2sEA43xglpnybzgJS BdIwve0AE0RiL6PEnrvrWeFaZsxYxAIylk1AW+LPFlGQBhEBe4nNsw+ATWIWuM4o8ezWDFaQ hLCAtcStP5/BprIIqEpcXHWcHcTmFbCVuLToEhvENnmJ1RsOMIPM5ASK3/9dCTJHQuA2m8TX 3W+ZIGpcJJYfvc0KYQtLvDq+hR3ClpJ42d/GDvFmtcTH/cwQ4Q5GiRffbSFsY4mb6zeAQ4JZ QFNi/S59iLCixM7fcxlBbGYBPol3X3uggcUr0dEmBFGiKtF36TDUAdISXe0foJZ6SNyZPIUd EiITGCWu/PjFOIFRbhbChgWMjKsYxVILinPTU4sNC4yQo2sTIzgpapntYFx0zucQowAHoxIP b4bG1Vgh1sSy4srcQ4wSHMxKIrx7rl+JFeJNSaysSi3Kjy8qzUktPsRoCgzHicxSosn5wISd VxJvaGpkbGxsYWJmbmZqrCTOy/HjYqyQQHpiSWp2ampBahFMHxMHp1QDo/nTRyIim9PMt9z5 FqC8c12IttVymXnGm6YtrBKPXvCFLVlZ9McntVfSd7yOMGTcflTw7W40Y8wk5myZMNsDH+4c 0Hnu4/rkT9rDa7onav79/SP3rt408+mdy/OuNXR1v+61NvkhfKZ0xdPKufvkJvfMm6NYqvah 8vivpSn1Dz/4Bf1V2yR6cLISS3FGoqEWc1FxIgBUxvjsoAMAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrBLMWRmVeSWpSXmKPExsWy7bCSnO4mhWuxBhfeS1gcfjyJ3aJ58Xo2 i5WrjzJZXL97i9liz96TLBaXd81hs/g/6zmrxY/p9Rb/37SwWGz5d4TV4tL7DywO3B47Z91l 99g/dw27x+6bDWwefVtWMXqs33KVxWPz6WqPz5vkPA5tf8PmcfvZNpYAzigum5TUnMyy1CJ9 uwSujKZlv5kKvrFWtC07wdjA2MDaxcjJISFgIjG97QBTFyMXh5DAbkaJh9MPMEIkpCWOnTjD 3MXIAWQLSxw+XAxR84FR4vT7tUwgcTYBbYk/W0RBykUEHCV6dx1mAalhFnjMKHHi/BOwOcIC 1hK3/nxmAbFZBFQlLq46zg5i8wrYSlxadIkNYpe8xOoNB8B2cQLF7/+uBAkLCdhIXD3xgnUC I98CRoZVjJKpBcW56bnFhgWGeanlesWJucWleel6yfm5mxjB4auluYPx8pL4Q4wCHIxKPLwZ GldjhVgTy4orcw8xSnAwK4nw7rl+JVaINyWxsiq1KD++qDQntfgQozQHi5I479O8Y5FCAumJ JanZqakFqUUwWSYOTqkGxroHxh09l9XsLoe/NjVauiUgJ7tR2Vldmt2uLT209eCeyQt6fTN7 JY/1v3C9GNayMSr19QPG9/NX1zyd76qsdP/NJK3Ld7i6tyfMPcv227OpLnGbeSfXrUVrtjBa cTxI4rp+0JWZw7Ijl8NLQuPqnpyIKbsOuRw5sZ/J+OdzpvVNFcmlmU+ylFiKMxINtZiLihMB Or62olsCAAA= X-CMS-MailID: 20191121052922epcas1p4c3957530e6d0ea89e1fc09322e999fb9 X-Msg-Generator: CA X-Sendblock-Type: SVC_REQ_APPROVE CMS-TYPE: 101P DLP-Filter: Pass X-CFilter-Loop: Reflected X-CMS-RootMailID: 20191121052922epcas1p4c3957530e6d0ea89e1fc09322e999fb9 References: <20191121052618.31117-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 9d61ef301811..41a69751840c 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