18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/fs/fat/dir.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * directory handling functions for fat-based filesystems 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Written 1992,1993 by Werner Almesberger 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Hidden files 1995 by Albert Cahalan <albert@ccs.neu.edu> <adc@coe.neu.edu> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * VFAT extensions by Gordon Chaffee <chaffee@plateau.cs.berkeley.edu> 128c2ecf20Sopenharmony_ci * Merged with msdos fs by Henrik Storner <storner@osiris.ping.dk> 138c2ecf20Sopenharmony_ci * Rewritten for constant inumbers. Plugged buffer overrun in readdir(). AV 148c2ecf20Sopenharmony_ci * Short name translation 1999, 2001 by Wolfram Pienkoss <wp@bszh.de> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <linux/slab.h> 188c2ecf20Sopenharmony_ci#include <linux/compat.h> 198c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 208c2ecf20Sopenharmony_ci#include <linux/iversion.h> 218c2ecf20Sopenharmony_ci#include "fat.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * Maximum buffer size of short name. 258c2ecf20Sopenharmony_ci * [(MSDOS_NAME + '.') * max one char + nul] 268c2ecf20Sopenharmony_ci * For msdos style, ['.' (hidden) + MSDOS_NAME + '.' + nul] 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_ci#define FAT_MAX_SHORT_SIZE ((MSDOS_NAME + 1) * NLS_MAX_CHARSET_SIZE + 1) 298c2ecf20Sopenharmony_ci/* 308c2ecf20Sopenharmony_ci * Maximum buffer size of unicode chars from slots. 318c2ecf20Sopenharmony_ci * [(max longname slots * 13 (size in a slot) + nul) * sizeof(wchar_t)] 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci#define FAT_MAX_UNI_CHARS ((MSDOS_SLOTS - 1) * 13 + 1) 348c2ecf20Sopenharmony_ci#define FAT_MAX_UNI_SIZE (FAT_MAX_UNI_CHARS * sizeof(wchar_t)) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic inline unsigned char fat_tolower(unsigned char c) 378c2ecf20Sopenharmony_ci{ 388c2ecf20Sopenharmony_ci return ((c >= 'A') && (c <= 'Z')) ? c+32 : c; 398c2ecf20Sopenharmony_ci} 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic inline loff_t fat_make_i_pos(struct super_block *sb, 428c2ecf20Sopenharmony_ci struct buffer_head *bh, 438c2ecf20Sopenharmony_ci struct msdos_dir_entry *de) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci return ((loff_t)bh->b_blocknr << MSDOS_SB(sb)->dir_per_block_bits) 468c2ecf20Sopenharmony_ci | (de - (struct msdos_dir_entry *)bh->b_data); 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic inline void fat_dir_readahead(struct inode *dir, sector_t iblock, 508c2ecf20Sopenharmony_ci sector_t phys) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 538c2ecf20Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 548c2ecf20Sopenharmony_ci struct buffer_head *bh; 558c2ecf20Sopenharmony_ci int sec; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci /* This is not a first sector of cluster, or sec_per_clus == 1 */ 588c2ecf20Sopenharmony_ci if ((iblock & (sbi->sec_per_clus - 1)) || sbi->sec_per_clus == 1) 598c2ecf20Sopenharmony_ci return; 608c2ecf20Sopenharmony_ci /* root dir of FAT12/FAT16 */ 618c2ecf20Sopenharmony_ci if (!is_fat32(sbi) && (dir->i_ino == MSDOS_ROOT_INO)) 628c2ecf20Sopenharmony_ci return; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci bh = sb_find_get_block(sb, phys); 658c2ecf20Sopenharmony_ci if (bh == NULL || !buffer_uptodate(bh)) { 668c2ecf20Sopenharmony_ci for (sec = 0; sec < sbi->sec_per_clus; sec++) 678c2ecf20Sopenharmony_ci sb_breadahead(sb, phys + sec); 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci brelse(bh); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* Returns the inode number of the directory entry at offset pos. If bh is 738c2ecf20Sopenharmony_ci non-NULL, it is brelse'd before. Pos is incremented. The buffer header is 748c2ecf20Sopenharmony_ci returned in bh. 758c2ecf20Sopenharmony_ci AV. Most often we do it item-by-item. Makes sense to optimize. 768c2ecf20Sopenharmony_ci AV. OK, there we go: if both bh and de are non-NULL we assume that we just 778c2ecf20Sopenharmony_ci AV. want the next entry (took one explicit de=NULL in vfat/namei.c). 788c2ecf20Sopenharmony_ci AV. It's done in fat_get_entry() (inlined), here the slow case lives. 798c2ecf20Sopenharmony_ci AV. Additionally, when we return -1 (i.e. reached the end of directory) 808c2ecf20Sopenharmony_ci AV. we make bh NULL. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_cistatic int fat__get_entry(struct inode *dir, loff_t *pos, 838c2ecf20Sopenharmony_ci struct buffer_head **bh, struct msdos_dir_entry **de) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 868c2ecf20Sopenharmony_ci sector_t phys, iblock; 878c2ecf20Sopenharmony_ci unsigned long mapped_blocks; 888c2ecf20Sopenharmony_ci int err, offset; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cinext: 918c2ecf20Sopenharmony_ci brelse(*bh); 928c2ecf20Sopenharmony_ci *bh = NULL; 938c2ecf20Sopenharmony_ci iblock = *pos >> sb->s_blocksize_bits; 948c2ecf20Sopenharmony_ci err = fat_bmap(dir, iblock, &phys, &mapped_blocks, 0, false); 958c2ecf20Sopenharmony_ci if (err || !phys) 968c2ecf20Sopenharmony_ci return -1; /* beyond EOF or error */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci fat_dir_readahead(dir, iblock, phys); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci *bh = sb_bread(sb, phys); 1018c2ecf20Sopenharmony_ci if (*bh == NULL) { 1028c2ecf20Sopenharmony_ci fat_msg_ratelimit(sb, KERN_ERR, 1038c2ecf20Sopenharmony_ci "Directory bread(block %llu) failed", (llu)phys); 1048c2ecf20Sopenharmony_ci /* skip this block */ 1058c2ecf20Sopenharmony_ci *pos = (iblock + 1) << sb->s_blocksize_bits; 1068c2ecf20Sopenharmony_ci goto next; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci offset = *pos & (sb->s_blocksize - 1); 1108c2ecf20Sopenharmony_ci *pos += sizeof(struct msdos_dir_entry); 1118c2ecf20Sopenharmony_ci *de = (struct msdos_dir_entry *)((*bh)->b_data + offset); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return 0; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic inline int fat_get_entry(struct inode *dir, loff_t *pos, 1178c2ecf20Sopenharmony_ci struct buffer_head **bh, 1188c2ecf20Sopenharmony_ci struct msdos_dir_entry **de) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci /* Fast stuff first */ 1218c2ecf20Sopenharmony_ci if (*bh && *de && 1228c2ecf20Sopenharmony_ci (*de - (struct msdos_dir_entry *)(*bh)->b_data) < 1238c2ecf20Sopenharmony_ci MSDOS_SB(dir->i_sb)->dir_per_block - 1) { 1248c2ecf20Sopenharmony_ci *pos += sizeof(struct msdos_dir_entry); 1258c2ecf20Sopenharmony_ci (*de)++; 1268c2ecf20Sopenharmony_ci return 0; 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci return fat__get_entry(dir, pos, bh, de); 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* 1328c2ecf20Sopenharmony_ci * Convert Unicode 16 to UTF-8, translated Unicode, or ASCII. 1338c2ecf20Sopenharmony_ci * If uni_xlate is enabled and we can't get a 1:1 conversion, use a 1348c2ecf20Sopenharmony_ci * colon as an escape character since it is normally invalid on the vfat 1358c2ecf20Sopenharmony_ci * filesystem. The following four characters are the hexadecimal digits 1368c2ecf20Sopenharmony_ci * of Unicode value. This lets us do a full dump and restore of Unicode 1378c2ecf20Sopenharmony_ci * filenames. We could get into some trouble with long Unicode names, 1388c2ecf20Sopenharmony_ci * but ignore that right now. 1398c2ecf20Sopenharmony_ci * Ahem... Stack smashing in ring 0 isn't fun. Fixed. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_cistatic int uni16_to_x8(struct super_block *sb, unsigned char *ascii, 1428c2ecf20Sopenharmony_ci const wchar_t *uni, int len, struct nls_table *nls) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci int uni_xlate = MSDOS_SB(sb)->options.unicode_xlate; 1458c2ecf20Sopenharmony_ci const wchar_t *ip; 1468c2ecf20Sopenharmony_ci wchar_t ec; 1478c2ecf20Sopenharmony_ci unsigned char *op; 1488c2ecf20Sopenharmony_ci int charlen; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci ip = uni; 1518c2ecf20Sopenharmony_ci op = ascii; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci while (*ip && ((len - NLS_MAX_CHARSET_SIZE) > 0)) { 1548c2ecf20Sopenharmony_ci ec = *ip++; 1558c2ecf20Sopenharmony_ci charlen = nls->uni2char(ec, op, NLS_MAX_CHARSET_SIZE); 1568c2ecf20Sopenharmony_ci if (charlen > 0) { 1578c2ecf20Sopenharmony_ci op += charlen; 1588c2ecf20Sopenharmony_ci len -= charlen; 1598c2ecf20Sopenharmony_ci } else { 1608c2ecf20Sopenharmony_ci if (uni_xlate == 1) { 1618c2ecf20Sopenharmony_ci *op++ = ':'; 1628c2ecf20Sopenharmony_ci op = hex_byte_pack(op, ec >> 8); 1638c2ecf20Sopenharmony_ci op = hex_byte_pack(op, ec); 1648c2ecf20Sopenharmony_ci len -= 5; 1658c2ecf20Sopenharmony_ci } else { 1668c2ecf20Sopenharmony_ci *op++ = '?'; 1678c2ecf20Sopenharmony_ci len--; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (unlikely(*ip)) { 1738c2ecf20Sopenharmony_ci fat_msg(sb, KERN_WARNING, 1748c2ecf20Sopenharmony_ci "filename was truncated while converting."); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci *op = 0; 1788c2ecf20Sopenharmony_ci return op - ascii; 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic inline int fat_uni_to_x8(struct super_block *sb, const wchar_t *uni, 1828c2ecf20Sopenharmony_ci unsigned char *buf, int size) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 1858c2ecf20Sopenharmony_ci if (sbi->options.utf8) 1868c2ecf20Sopenharmony_ci return utf16s_to_utf8s(uni, FAT_MAX_UNI_CHARS, 1878c2ecf20Sopenharmony_ci UTF16_HOST_ENDIAN, buf, size); 1888c2ecf20Sopenharmony_ci else 1898c2ecf20Sopenharmony_ci return uni16_to_x8(sb, buf, uni, size, sbi->nls_io); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic inline int 1938c2ecf20Sopenharmony_cifat_short2uni(struct nls_table *t, unsigned char *c, int clen, wchar_t *uni) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci int charlen; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci charlen = t->char2uni(c, clen, uni); 1988c2ecf20Sopenharmony_ci if (charlen < 0) { 1998c2ecf20Sopenharmony_ci *uni = 0x003f; /* a question mark */ 2008c2ecf20Sopenharmony_ci charlen = 1; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci return charlen; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic inline int 2068c2ecf20Sopenharmony_cifat_short2lower_uni(struct nls_table *t, unsigned char *c, 2078c2ecf20Sopenharmony_ci int clen, wchar_t *uni) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci int charlen; 2108c2ecf20Sopenharmony_ci wchar_t wc; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci charlen = t->char2uni(c, clen, &wc); 2138c2ecf20Sopenharmony_ci if (charlen < 0) { 2148c2ecf20Sopenharmony_ci *uni = 0x003f; /* a question mark */ 2158c2ecf20Sopenharmony_ci charlen = 1; 2168c2ecf20Sopenharmony_ci } else if (charlen <= 1) { 2178c2ecf20Sopenharmony_ci unsigned char nc = t->charset2lower[*c]; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (!nc) 2208c2ecf20Sopenharmony_ci nc = *c; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci charlen = t->char2uni(&nc, 1, uni); 2238c2ecf20Sopenharmony_ci if (charlen < 0) { 2248c2ecf20Sopenharmony_ci *uni = 0x003f; /* a question mark */ 2258c2ecf20Sopenharmony_ci charlen = 1; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci } else 2288c2ecf20Sopenharmony_ci *uni = wc; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return charlen; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic inline int 2348c2ecf20Sopenharmony_cifat_shortname2uni(struct nls_table *nls, unsigned char *buf, int buf_size, 2358c2ecf20Sopenharmony_ci wchar_t *uni_buf, unsigned short opt, int lower) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci int len = 0; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (opt & VFAT_SFN_DISPLAY_LOWER) 2408c2ecf20Sopenharmony_ci len = fat_short2lower_uni(nls, buf, buf_size, uni_buf); 2418c2ecf20Sopenharmony_ci else if (opt & VFAT_SFN_DISPLAY_WIN95) 2428c2ecf20Sopenharmony_ci len = fat_short2uni(nls, buf, buf_size, uni_buf); 2438c2ecf20Sopenharmony_ci else if (opt & VFAT_SFN_DISPLAY_WINNT) { 2448c2ecf20Sopenharmony_ci if (lower) 2458c2ecf20Sopenharmony_ci len = fat_short2lower_uni(nls, buf, buf_size, uni_buf); 2468c2ecf20Sopenharmony_ci else 2478c2ecf20Sopenharmony_ci len = fat_short2uni(nls, buf, buf_size, uni_buf); 2488c2ecf20Sopenharmony_ci } else 2498c2ecf20Sopenharmony_ci len = fat_short2uni(nls, buf, buf_size, uni_buf); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return len; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic inline int fat_name_match(struct msdos_sb_info *sbi, 2558c2ecf20Sopenharmony_ci const unsigned char *a, int a_len, 2568c2ecf20Sopenharmony_ci const unsigned char *b, int b_len) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci if (a_len != b_len) 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (sbi->options.name_check != 's') 2628c2ecf20Sopenharmony_ci return !nls_strnicmp(sbi->nls_io, a, b, a_len); 2638c2ecf20Sopenharmony_ci else 2648c2ecf20Sopenharmony_ci return !memcmp(a, b, a_len); 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cienum { PARSE_INVALID = 1, PARSE_NOT_LONGNAME, PARSE_EOF, }; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/** 2708c2ecf20Sopenharmony_ci * fat_parse_long - Parse extended directory entry. 2718c2ecf20Sopenharmony_ci * 2728c2ecf20Sopenharmony_ci * This function returns zero on success, negative value on error, or one of 2738c2ecf20Sopenharmony_ci * the following: 2748c2ecf20Sopenharmony_ci * 2758c2ecf20Sopenharmony_ci * %PARSE_INVALID - Directory entry is invalid. 2768c2ecf20Sopenharmony_ci * %PARSE_NOT_LONGNAME - Directory entry does not contain longname. 2778c2ecf20Sopenharmony_ci * %PARSE_EOF - Directory has no more entries. 2788c2ecf20Sopenharmony_ci */ 2798c2ecf20Sopenharmony_cistatic int fat_parse_long(struct inode *dir, loff_t *pos, 2808c2ecf20Sopenharmony_ci struct buffer_head **bh, struct msdos_dir_entry **de, 2818c2ecf20Sopenharmony_ci wchar_t **unicode, unsigned char *nr_slots) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci struct msdos_dir_slot *ds; 2848c2ecf20Sopenharmony_ci unsigned char id, slot, slots, alias_checksum; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (!*unicode) { 2878c2ecf20Sopenharmony_ci *unicode = __getname(); 2888c2ecf20Sopenharmony_ci if (!*unicode) { 2898c2ecf20Sopenharmony_ci brelse(*bh); 2908c2ecf20Sopenharmony_ci return -ENOMEM; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci } 2938c2ecf20Sopenharmony_ciparse_long: 2948c2ecf20Sopenharmony_ci ds = (struct msdos_dir_slot *)*de; 2958c2ecf20Sopenharmony_ci id = ds->id; 2968c2ecf20Sopenharmony_ci if (!(id & 0x40)) 2978c2ecf20Sopenharmony_ci return PARSE_INVALID; 2988c2ecf20Sopenharmony_ci slots = id & ~0x40; 2998c2ecf20Sopenharmony_ci if (slots > 20 || !slots) /* ceil(256 * 2 / 26) */ 3008c2ecf20Sopenharmony_ci return PARSE_INVALID; 3018c2ecf20Sopenharmony_ci *nr_slots = slots; 3028c2ecf20Sopenharmony_ci alias_checksum = ds->alias_checksum; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci slot = slots; 3058c2ecf20Sopenharmony_ci while (1) { 3068c2ecf20Sopenharmony_ci int offset; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci slot--; 3098c2ecf20Sopenharmony_ci offset = slot * 13; 3108c2ecf20Sopenharmony_ci fat16_towchar(*unicode + offset, ds->name0_4, 5); 3118c2ecf20Sopenharmony_ci fat16_towchar(*unicode + offset + 5, ds->name5_10, 6); 3128c2ecf20Sopenharmony_ci fat16_towchar(*unicode + offset + 11, ds->name11_12, 2); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (ds->id & 0x40) 3158c2ecf20Sopenharmony_ci (*unicode)[offset + 13] = 0; 3168c2ecf20Sopenharmony_ci if (fat_get_entry(dir, pos, bh, de) < 0) 3178c2ecf20Sopenharmony_ci return PARSE_EOF; 3188c2ecf20Sopenharmony_ci if (slot == 0) 3198c2ecf20Sopenharmony_ci break; 3208c2ecf20Sopenharmony_ci ds = (struct msdos_dir_slot *)*de; 3218c2ecf20Sopenharmony_ci if (ds->attr != ATTR_EXT) 3228c2ecf20Sopenharmony_ci return PARSE_NOT_LONGNAME; 3238c2ecf20Sopenharmony_ci if ((ds->id & ~0x40) != slot) 3248c2ecf20Sopenharmony_ci goto parse_long; 3258c2ecf20Sopenharmony_ci if (ds->alias_checksum != alias_checksum) 3268c2ecf20Sopenharmony_ci goto parse_long; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci if ((*de)->name[0] == DELETED_FLAG) 3298c2ecf20Sopenharmony_ci return PARSE_INVALID; 3308c2ecf20Sopenharmony_ci if ((*de)->attr == ATTR_EXT) 3318c2ecf20Sopenharmony_ci goto parse_long; 3328c2ecf20Sopenharmony_ci if (IS_FREE((*de)->name) || ((*de)->attr & ATTR_VOLUME)) 3338c2ecf20Sopenharmony_ci return PARSE_INVALID; 3348c2ecf20Sopenharmony_ci if (fat_checksum((*de)->name) != alias_checksum) 3358c2ecf20Sopenharmony_ci *nr_slots = 0; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci return 0; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/** 3418c2ecf20Sopenharmony_ci * fat_parse_short - Parse MS-DOS (short) directory entry. 3428c2ecf20Sopenharmony_ci * @sb: superblock 3438c2ecf20Sopenharmony_ci * @de: directory entry to parse 3448c2ecf20Sopenharmony_ci * @name: FAT_MAX_SHORT_SIZE array in which to place extracted name 3458c2ecf20Sopenharmony_ci * @dot_hidden: Nonzero == prepend '.' to names with ATTR_HIDDEN 3468c2ecf20Sopenharmony_ci * 3478c2ecf20Sopenharmony_ci * Returns the number of characters extracted into 'name'. 3488c2ecf20Sopenharmony_ci */ 3498c2ecf20Sopenharmony_cistatic int fat_parse_short(struct super_block *sb, 3508c2ecf20Sopenharmony_ci const struct msdos_dir_entry *de, 3518c2ecf20Sopenharmony_ci unsigned char *name, int dot_hidden) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci const struct msdos_sb_info *sbi = MSDOS_SB(sb); 3548c2ecf20Sopenharmony_ci int isvfat = sbi->options.isvfat; 3558c2ecf20Sopenharmony_ci int nocase = sbi->options.nocase; 3568c2ecf20Sopenharmony_ci unsigned short opt_shortname = sbi->options.shortname; 3578c2ecf20Sopenharmony_ci struct nls_table *nls_disk = sbi->nls_disk; 3588c2ecf20Sopenharmony_ci wchar_t uni_name[14]; 3598c2ecf20Sopenharmony_ci unsigned char c, work[MSDOS_NAME]; 3608c2ecf20Sopenharmony_ci unsigned char *ptname = name; 3618c2ecf20Sopenharmony_ci int chi, chl, i, j, k; 3628c2ecf20Sopenharmony_ci int dotoffset = 0; 3638c2ecf20Sopenharmony_ci int name_len = 0, uni_len = 0; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci if (!isvfat && dot_hidden && (de->attr & ATTR_HIDDEN)) { 3668c2ecf20Sopenharmony_ci *ptname++ = '.'; 3678c2ecf20Sopenharmony_ci dotoffset = 1; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci memcpy(work, de->name, sizeof(work)); 3718c2ecf20Sopenharmony_ci /* For an explanation of the special treatment of 0x05 in 3728c2ecf20Sopenharmony_ci * filenames, see msdos_format_name in namei_msdos.c 3738c2ecf20Sopenharmony_ci */ 3748c2ecf20Sopenharmony_ci if (work[0] == 0x05) 3758c2ecf20Sopenharmony_ci work[0] = 0xE5; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* Filename */ 3788c2ecf20Sopenharmony_ci for (i = 0, j = 0; i < 8;) { 3798c2ecf20Sopenharmony_ci c = work[i]; 3808c2ecf20Sopenharmony_ci if (!c) 3818c2ecf20Sopenharmony_ci break; 3828c2ecf20Sopenharmony_ci chl = fat_shortname2uni(nls_disk, &work[i], 8 - i, 3838c2ecf20Sopenharmony_ci &uni_name[j++], opt_shortname, 3848c2ecf20Sopenharmony_ci de->lcase & CASE_LOWER_BASE); 3858c2ecf20Sopenharmony_ci if (chl <= 1) { 3868c2ecf20Sopenharmony_ci if (!isvfat) 3878c2ecf20Sopenharmony_ci ptname[i] = nocase ? c : fat_tolower(c); 3888c2ecf20Sopenharmony_ci i++; 3898c2ecf20Sopenharmony_ci if (c != ' ') { 3908c2ecf20Sopenharmony_ci name_len = i; 3918c2ecf20Sopenharmony_ci uni_len = j; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci } else { 3948c2ecf20Sopenharmony_ci uni_len = j; 3958c2ecf20Sopenharmony_ci if (isvfat) 3968c2ecf20Sopenharmony_ci i += min(chl, 8-i); 3978c2ecf20Sopenharmony_ci else { 3988c2ecf20Sopenharmony_ci for (chi = 0; chi < chl && i < 8; chi++, i++) 3998c2ecf20Sopenharmony_ci ptname[i] = work[i]; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci if (chl) 4028c2ecf20Sopenharmony_ci name_len = i; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci i = name_len; 4078c2ecf20Sopenharmony_ci j = uni_len; 4088c2ecf20Sopenharmony_ci fat_short2uni(nls_disk, ".", 1, &uni_name[j++]); 4098c2ecf20Sopenharmony_ci if (!isvfat) 4108c2ecf20Sopenharmony_ci ptname[i] = '.'; 4118c2ecf20Sopenharmony_ci i++; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* Extension */ 4148c2ecf20Sopenharmony_ci for (k = 8; k < MSDOS_NAME;) { 4158c2ecf20Sopenharmony_ci c = work[k]; 4168c2ecf20Sopenharmony_ci if (!c) 4178c2ecf20Sopenharmony_ci break; 4188c2ecf20Sopenharmony_ci chl = fat_shortname2uni(nls_disk, &work[k], MSDOS_NAME - k, 4198c2ecf20Sopenharmony_ci &uni_name[j++], opt_shortname, 4208c2ecf20Sopenharmony_ci de->lcase & CASE_LOWER_EXT); 4218c2ecf20Sopenharmony_ci if (chl <= 1) { 4228c2ecf20Sopenharmony_ci k++; 4238c2ecf20Sopenharmony_ci if (!isvfat) 4248c2ecf20Sopenharmony_ci ptname[i] = nocase ? c : fat_tolower(c); 4258c2ecf20Sopenharmony_ci i++; 4268c2ecf20Sopenharmony_ci if (c != ' ') { 4278c2ecf20Sopenharmony_ci name_len = i; 4288c2ecf20Sopenharmony_ci uni_len = j; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci } else { 4318c2ecf20Sopenharmony_ci uni_len = j; 4328c2ecf20Sopenharmony_ci if (isvfat) { 4338c2ecf20Sopenharmony_ci int offset = min(chl, MSDOS_NAME-k); 4348c2ecf20Sopenharmony_ci k += offset; 4358c2ecf20Sopenharmony_ci i += offset; 4368c2ecf20Sopenharmony_ci } else { 4378c2ecf20Sopenharmony_ci for (chi = 0; chi < chl && k < MSDOS_NAME; 4388c2ecf20Sopenharmony_ci chi++, i++, k++) { 4398c2ecf20Sopenharmony_ci ptname[i] = work[k]; 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci if (chl) 4438c2ecf20Sopenharmony_ci name_len = i; 4448c2ecf20Sopenharmony_ci } 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (name_len > 0) { 4488c2ecf20Sopenharmony_ci name_len += dotoffset; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (sbi->options.isvfat) { 4518c2ecf20Sopenharmony_ci uni_name[uni_len] = 0x0000; 4528c2ecf20Sopenharmony_ci name_len = fat_uni_to_x8(sb, uni_name, name, 4538c2ecf20Sopenharmony_ci FAT_MAX_SHORT_SIZE); 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return name_len; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci/* 4618c2ecf20Sopenharmony_ci * Return values: negative -> error/not found, 0 -> found. 4628c2ecf20Sopenharmony_ci */ 4638c2ecf20Sopenharmony_ciint fat_search_long(struct inode *inode, const unsigned char *name, 4648c2ecf20Sopenharmony_ci int name_len, struct fat_slot_info *sinfo) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 4678c2ecf20Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 4688c2ecf20Sopenharmony_ci struct buffer_head *bh = NULL; 4698c2ecf20Sopenharmony_ci struct msdos_dir_entry *de; 4708c2ecf20Sopenharmony_ci unsigned char nr_slots; 4718c2ecf20Sopenharmony_ci wchar_t *unicode = NULL; 4728c2ecf20Sopenharmony_ci unsigned char bufname[FAT_MAX_SHORT_SIZE]; 4738c2ecf20Sopenharmony_ci loff_t cpos = 0; 4748c2ecf20Sopenharmony_ci int err, len; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci err = -ENOENT; 4778c2ecf20Sopenharmony_ci while (1) { 4788c2ecf20Sopenharmony_ci if (fat_get_entry(inode, &cpos, &bh, &de) == -1) 4798c2ecf20Sopenharmony_ci goto end_of_dir; 4808c2ecf20Sopenharmony_ciparse_record: 4818c2ecf20Sopenharmony_ci nr_slots = 0; 4828c2ecf20Sopenharmony_ci if (de->name[0] == DELETED_FLAG) 4838c2ecf20Sopenharmony_ci continue; 4848c2ecf20Sopenharmony_ci if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME)) 4858c2ecf20Sopenharmony_ci continue; 4868c2ecf20Sopenharmony_ci if (de->attr != ATTR_EXT && IS_FREE(de->name)) 4878c2ecf20Sopenharmony_ci continue; 4888c2ecf20Sopenharmony_ci if (de->attr == ATTR_EXT) { 4898c2ecf20Sopenharmony_ci int status = fat_parse_long(inode, &cpos, &bh, &de, 4908c2ecf20Sopenharmony_ci &unicode, &nr_slots); 4918c2ecf20Sopenharmony_ci if (status < 0) { 4928c2ecf20Sopenharmony_ci err = status; 4938c2ecf20Sopenharmony_ci goto end_of_dir; 4948c2ecf20Sopenharmony_ci } else if (status == PARSE_INVALID) 4958c2ecf20Sopenharmony_ci continue; 4968c2ecf20Sopenharmony_ci else if (status == PARSE_NOT_LONGNAME) 4978c2ecf20Sopenharmony_ci goto parse_record; 4988c2ecf20Sopenharmony_ci else if (status == PARSE_EOF) 4998c2ecf20Sopenharmony_ci goto end_of_dir; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* Never prepend '.' to hidden files here. 5038c2ecf20Sopenharmony_ci * That is done only for msdos mounts (and only when 5048c2ecf20Sopenharmony_ci * 'dotsOK=yes'); if we are executing here, it is in the 5058c2ecf20Sopenharmony_ci * context of a vfat mount. 5068c2ecf20Sopenharmony_ci */ 5078c2ecf20Sopenharmony_ci len = fat_parse_short(sb, de, bufname, 0); 5088c2ecf20Sopenharmony_ci if (len == 0) 5098c2ecf20Sopenharmony_ci continue; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* Compare shortname */ 5128c2ecf20Sopenharmony_ci if (fat_name_match(sbi, name, name_len, bufname, len)) 5138c2ecf20Sopenharmony_ci goto found; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (nr_slots) { 5168c2ecf20Sopenharmony_ci void *longname = unicode + FAT_MAX_UNI_CHARS; 5178c2ecf20Sopenharmony_ci int size = PATH_MAX - FAT_MAX_UNI_SIZE; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* Compare longname */ 5208c2ecf20Sopenharmony_ci len = fat_uni_to_x8(sb, unicode, longname, size); 5218c2ecf20Sopenharmony_ci if (fat_name_match(sbi, name, name_len, longname, len)) 5228c2ecf20Sopenharmony_ci goto found; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cifound: 5278c2ecf20Sopenharmony_ci nr_slots++; /* include the de */ 5288c2ecf20Sopenharmony_ci sinfo->slot_off = cpos - nr_slots * sizeof(*de); 5298c2ecf20Sopenharmony_ci sinfo->nr_slots = nr_slots; 5308c2ecf20Sopenharmony_ci sinfo->de = de; 5318c2ecf20Sopenharmony_ci sinfo->bh = bh; 5328c2ecf20Sopenharmony_ci sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de); 5338c2ecf20Sopenharmony_ci err = 0; 5348c2ecf20Sopenharmony_ciend_of_dir: 5358c2ecf20Sopenharmony_ci if (unicode) 5368c2ecf20Sopenharmony_ci __putname(unicode); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci return err; 5398c2ecf20Sopenharmony_ci} 5408c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_search_long); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistruct fat_ioctl_filldir_callback { 5438c2ecf20Sopenharmony_ci struct dir_context ctx; 5448c2ecf20Sopenharmony_ci void __user *dirent; 5458c2ecf20Sopenharmony_ci int result; 5468c2ecf20Sopenharmony_ci /* for dir ioctl */ 5478c2ecf20Sopenharmony_ci const char *longname; 5488c2ecf20Sopenharmony_ci int long_len; 5498c2ecf20Sopenharmony_ci const char *shortname; 5508c2ecf20Sopenharmony_ci int short_len; 5518c2ecf20Sopenharmony_ci}; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_cistatic int __fat_readdir(struct inode *inode, struct file *file, 5548c2ecf20Sopenharmony_ci struct dir_context *ctx, int short_only, 5558c2ecf20Sopenharmony_ci struct fat_ioctl_filldir_callback *both) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct super_block *sb = inode->i_sb; 5588c2ecf20Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 5598c2ecf20Sopenharmony_ci struct buffer_head *bh; 5608c2ecf20Sopenharmony_ci struct msdos_dir_entry *de; 5618c2ecf20Sopenharmony_ci unsigned char nr_slots; 5628c2ecf20Sopenharmony_ci wchar_t *unicode = NULL; 5638c2ecf20Sopenharmony_ci unsigned char bufname[FAT_MAX_SHORT_SIZE]; 5648c2ecf20Sopenharmony_ci int isvfat = sbi->options.isvfat; 5658c2ecf20Sopenharmony_ci const char *fill_name = NULL; 5668c2ecf20Sopenharmony_ci int fake_offset = 0; 5678c2ecf20Sopenharmony_ci loff_t cpos; 5688c2ecf20Sopenharmony_ci int short_len = 0, fill_len = 0; 5698c2ecf20Sopenharmony_ci int ret = 0; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci mutex_lock(&sbi->s_lock); 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci cpos = ctx->pos; 5748c2ecf20Sopenharmony_ci /* Fake . and .. for the root directory. */ 5758c2ecf20Sopenharmony_ci if (inode->i_ino == MSDOS_ROOT_INO) { 5768c2ecf20Sopenharmony_ci if (!dir_emit_dots(file, ctx)) 5778c2ecf20Sopenharmony_ci goto out; 5788c2ecf20Sopenharmony_ci if (ctx->pos == 2) { 5798c2ecf20Sopenharmony_ci fake_offset = 1; 5808c2ecf20Sopenharmony_ci cpos = 0; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci if (cpos & (sizeof(struct msdos_dir_entry) - 1)) { 5848c2ecf20Sopenharmony_ci ret = -ENOENT; 5858c2ecf20Sopenharmony_ci goto out; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci bh = NULL; 5898c2ecf20Sopenharmony_ciget_new: 5908c2ecf20Sopenharmony_ci if (fat_get_entry(inode, &cpos, &bh, &de) == -1) 5918c2ecf20Sopenharmony_ci goto end_of_dir; 5928c2ecf20Sopenharmony_ciparse_record: 5938c2ecf20Sopenharmony_ci nr_slots = 0; 5948c2ecf20Sopenharmony_ci /* 5958c2ecf20Sopenharmony_ci * Check for long filename entry, but if short_only, we don't 5968c2ecf20Sopenharmony_ci * need to parse long filename. 5978c2ecf20Sopenharmony_ci */ 5988c2ecf20Sopenharmony_ci if (isvfat && !short_only) { 5998c2ecf20Sopenharmony_ci if (de->name[0] == DELETED_FLAG) 6008c2ecf20Sopenharmony_ci goto record_end; 6018c2ecf20Sopenharmony_ci if (de->attr != ATTR_EXT && (de->attr & ATTR_VOLUME)) 6028c2ecf20Sopenharmony_ci goto record_end; 6038c2ecf20Sopenharmony_ci if (de->attr != ATTR_EXT && IS_FREE(de->name)) 6048c2ecf20Sopenharmony_ci goto record_end; 6058c2ecf20Sopenharmony_ci } else { 6068c2ecf20Sopenharmony_ci if ((de->attr & ATTR_VOLUME) || IS_FREE(de->name)) 6078c2ecf20Sopenharmony_ci goto record_end; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci if (isvfat && de->attr == ATTR_EXT) { 6118c2ecf20Sopenharmony_ci int status = fat_parse_long(inode, &cpos, &bh, &de, 6128c2ecf20Sopenharmony_ci &unicode, &nr_slots); 6138c2ecf20Sopenharmony_ci if (status < 0) { 6148c2ecf20Sopenharmony_ci bh = NULL; 6158c2ecf20Sopenharmony_ci ret = status; 6168c2ecf20Sopenharmony_ci goto end_of_dir; 6178c2ecf20Sopenharmony_ci } else if (status == PARSE_INVALID) 6188c2ecf20Sopenharmony_ci goto record_end; 6198c2ecf20Sopenharmony_ci else if (status == PARSE_NOT_LONGNAME) 6208c2ecf20Sopenharmony_ci goto parse_record; 6218c2ecf20Sopenharmony_ci else if (status == PARSE_EOF) 6228c2ecf20Sopenharmony_ci goto end_of_dir; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (nr_slots) { 6258c2ecf20Sopenharmony_ci void *longname = unicode + FAT_MAX_UNI_CHARS; 6268c2ecf20Sopenharmony_ci int size = PATH_MAX - FAT_MAX_UNI_SIZE; 6278c2ecf20Sopenharmony_ci int len = fat_uni_to_x8(sb, unicode, longname, size); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci fill_name = longname; 6308c2ecf20Sopenharmony_ci fill_len = len; 6318c2ecf20Sopenharmony_ci /* !both && !short_only, so we don't need shortname. */ 6328c2ecf20Sopenharmony_ci if (!both) 6338c2ecf20Sopenharmony_ci goto start_filldir; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci short_len = fat_parse_short(sb, de, bufname, 6368c2ecf20Sopenharmony_ci sbi->options.dotsOK); 6378c2ecf20Sopenharmony_ci if (short_len == 0) 6388c2ecf20Sopenharmony_ci goto record_end; 6398c2ecf20Sopenharmony_ci /* hack for fat_ioctl_filldir() */ 6408c2ecf20Sopenharmony_ci both->longname = fill_name; 6418c2ecf20Sopenharmony_ci both->long_len = fill_len; 6428c2ecf20Sopenharmony_ci both->shortname = bufname; 6438c2ecf20Sopenharmony_ci both->short_len = short_len; 6448c2ecf20Sopenharmony_ci fill_name = NULL; 6458c2ecf20Sopenharmony_ci fill_len = 0; 6468c2ecf20Sopenharmony_ci goto start_filldir; 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci } 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci short_len = fat_parse_short(sb, de, bufname, sbi->options.dotsOK); 6518c2ecf20Sopenharmony_ci if (short_len == 0) 6528c2ecf20Sopenharmony_ci goto record_end; 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci fill_name = bufname; 6558c2ecf20Sopenharmony_ci fill_len = short_len; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistart_filldir: 6588c2ecf20Sopenharmony_ci ctx->pos = cpos - (nr_slots + 1) * sizeof(struct msdos_dir_entry); 6598c2ecf20Sopenharmony_ci if (fake_offset && ctx->pos < 2) 6608c2ecf20Sopenharmony_ci ctx->pos = 2; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci if (!memcmp(de->name, MSDOS_DOT, MSDOS_NAME)) { 6638c2ecf20Sopenharmony_ci if (!dir_emit_dot(file, ctx)) 6648c2ecf20Sopenharmony_ci goto fill_failed; 6658c2ecf20Sopenharmony_ci } else if (!memcmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) { 6668c2ecf20Sopenharmony_ci if (!dir_emit_dotdot(file, ctx)) 6678c2ecf20Sopenharmony_ci goto fill_failed; 6688c2ecf20Sopenharmony_ci } else { 6698c2ecf20Sopenharmony_ci unsigned long inum; 6708c2ecf20Sopenharmony_ci loff_t i_pos = fat_make_i_pos(sb, bh, de); 6718c2ecf20Sopenharmony_ci struct inode *tmp = fat_iget(sb, i_pos); 6728c2ecf20Sopenharmony_ci if (tmp) { 6738c2ecf20Sopenharmony_ci inum = tmp->i_ino; 6748c2ecf20Sopenharmony_ci iput(tmp); 6758c2ecf20Sopenharmony_ci } else 6768c2ecf20Sopenharmony_ci inum = iunique(sb, MSDOS_ROOT_INO); 6778c2ecf20Sopenharmony_ci if (!dir_emit(ctx, fill_name, fill_len, inum, 6788c2ecf20Sopenharmony_ci (de->attr & ATTR_DIR) ? DT_DIR : DT_REG)) 6798c2ecf20Sopenharmony_ci goto fill_failed; 6808c2ecf20Sopenharmony_ci } 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cirecord_end: 6838c2ecf20Sopenharmony_ci fake_offset = 0; 6848c2ecf20Sopenharmony_ci ctx->pos = cpos; 6858c2ecf20Sopenharmony_ci goto get_new; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ciend_of_dir: 6888c2ecf20Sopenharmony_ci if (fake_offset && cpos < 2) 6898c2ecf20Sopenharmony_ci ctx->pos = 2; 6908c2ecf20Sopenharmony_ci else 6918c2ecf20Sopenharmony_ci ctx->pos = cpos; 6928c2ecf20Sopenharmony_cifill_failed: 6938c2ecf20Sopenharmony_ci brelse(bh); 6948c2ecf20Sopenharmony_ci if (unicode) 6958c2ecf20Sopenharmony_ci __putname(unicode); 6968c2ecf20Sopenharmony_ciout: 6978c2ecf20Sopenharmony_ci mutex_unlock(&sbi->s_lock); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci return ret; 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic int fat_readdir(struct file *file, struct dir_context *ctx) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci return __fat_readdir(file_inode(file), file, ctx, 0, NULL); 7058c2ecf20Sopenharmony_ci} 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci#define FAT_IOCTL_FILLDIR_FUNC(func, dirent_type) \ 7088c2ecf20Sopenharmony_cistatic int func(struct dir_context *ctx, const char *name, int name_len, \ 7098c2ecf20Sopenharmony_ci loff_t offset, u64 ino, unsigned int d_type) \ 7108c2ecf20Sopenharmony_ci{ \ 7118c2ecf20Sopenharmony_ci struct fat_ioctl_filldir_callback *buf = \ 7128c2ecf20Sopenharmony_ci container_of(ctx, struct fat_ioctl_filldir_callback, ctx); \ 7138c2ecf20Sopenharmony_ci struct dirent_type __user *d1 = buf->dirent; \ 7148c2ecf20Sopenharmony_ci struct dirent_type __user *d2 = d1 + 1; \ 7158c2ecf20Sopenharmony_ci \ 7168c2ecf20Sopenharmony_ci if (buf->result) \ 7178c2ecf20Sopenharmony_ci return -EINVAL; \ 7188c2ecf20Sopenharmony_ci buf->result++; \ 7198c2ecf20Sopenharmony_ci \ 7208c2ecf20Sopenharmony_ci if (name != NULL) { \ 7218c2ecf20Sopenharmony_ci /* dirent has only short name */ \ 7228c2ecf20Sopenharmony_ci if (name_len >= sizeof(d1->d_name)) \ 7238c2ecf20Sopenharmony_ci name_len = sizeof(d1->d_name) - 1; \ 7248c2ecf20Sopenharmony_ci \ 7258c2ecf20Sopenharmony_ci if (put_user(0, d2->d_name) || \ 7268c2ecf20Sopenharmony_ci put_user(0, &d2->d_reclen) || \ 7278c2ecf20Sopenharmony_ci copy_to_user(d1->d_name, name, name_len) || \ 7288c2ecf20Sopenharmony_ci put_user(0, d1->d_name + name_len) || \ 7298c2ecf20Sopenharmony_ci put_user(name_len, &d1->d_reclen)) \ 7308c2ecf20Sopenharmony_ci goto efault; \ 7318c2ecf20Sopenharmony_ci } else { \ 7328c2ecf20Sopenharmony_ci /* dirent has short and long name */ \ 7338c2ecf20Sopenharmony_ci const char *longname = buf->longname; \ 7348c2ecf20Sopenharmony_ci int long_len = buf->long_len; \ 7358c2ecf20Sopenharmony_ci const char *shortname = buf->shortname; \ 7368c2ecf20Sopenharmony_ci int short_len = buf->short_len; \ 7378c2ecf20Sopenharmony_ci \ 7388c2ecf20Sopenharmony_ci if (long_len >= sizeof(d1->d_name)) \ 7398c2ecf20Sopenharmony_ci long_len = sizeof(d1->d_name) - 1; \ 7408c2ecf20Sopenharmony_ci if (short_len >= sizeof(d1->d_name)) \ 7418c2ecf20Sopenharmony_ci short_len = sizeof(d1->d_name) - 1; \ 7428c2ecf20Sopenharmony_ci \ 7438c2ecf20Sopenharmony_ci if (copy_to_user(d2->d_name, longname, long_len) || \ 7448c2ecf20Sopenharmony_ci put_user(0, d2->d_name + long_len) || \ 7458c2ecf20Sopenharmony_ci put_user(long_len, &d2->d_reclen) || \ 7468c2ecf20Sopenharmony_ci put_user(ino, &d2->d_ino) || \ 7478c2ecf20Sopenharmony_ci put_user(offset, &d2->d_off) || \ 7488c2ecf20Sopenharmony_ci copy_to_user(d1->d_name, shortname, short_len) || \ 7498c2ecf20Sopenharmony_ci put_user(0, d1->d_name + short_len) || \ 7508c2ecf20Sopenharmony_ci put_user(short_len, &d1->d_reclen)) \ 7518c2ecf20Sopenharmony_ci goto efault; \ 7528c2ecf20Sopenharmony_ci } \ 7538c2ecf20Sopenharmony_ci return 0; \ 7548c2ecf20Sopenharmony_ciefault: \ 7558c2ecf20Sopenharmony_ci buf->result = -EFAULT; \ 7568c2ecf20Sopenharmony_ci return -EFAULT; \ 7578c2ecf20Sopenharmony_ci} 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ciFAT_IOCTL_FILLDIR_FUNC(fat_ioctl_filldir, __fat_dirent) 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_cistatic int fat_ioctl_readdir(struct inode *inode, struct file *file, 7628c2ecf20Sopenharmony_ci void __user *dirent, filldir_t filldir, 7638c2ecf20Sopenharmony_ci int short_only, int both) 7648c2ecf20Sopenharmony_ci{ 7658c2ecf20Sopenharmony_ci struct fat_ioctl_filldir_callback buf = { 7668c2ecf20Sopenharmony_ci .ctx.actor = filldir, 7678c2ecf20Sopenharmony_ci .dirent = dirent 7688c2ecf20Sopenharmony_ci }; 7698c2ecf20Sopenharmony_ci int ret; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci buf.dirent = dirent; 7728c2ecf20Sopenharmony_ci buf.result = 0; 7738c2ecf20Sopenharmony_ci inode_lock_shared(inode); 7748c2ecf20Sopenharmony_ci buf.ctx.pos = file->f_pos; 7758c2ecf20Sopenharmony_ci ret = -ENOENT; 7768c2ecf20Sopenharmony_ci if (!IS_DEADDIR(inode)) { 7778c2ecf20Sopenharmony_ci ret = __fat_readdir(inode, file, &buf.ctx, 7788c2ecf20Sopenharmony_ci short_only, both ? &buf : NULL); 7798c2ecf20Sopenharmony_ci file->f_pos = buf.ctx.pos; 7808c2ecf20Sopenharmony_ci } 7818c2ecf20Sopenharmony_ci inode_unlock_shared(inode); 7828c2ecf20Sopenharmony_ci if (ret >= 0) 7838c2ecf20Sopenharmony_ci ret = buf.result; 7848c2ecf20Sopenharmony_ci return ret; 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cistatic long fat_dir_ioctl(struct file *filp, unsigned int cmd, 7888c2ecf20Sopenharmony_ci unsigned long arg) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci struct inode *inode = file_inode(filp); 7918c2ecf20Sopenharmony_ci struct __fat_dirent __user *d1 = (struct __fat_dirent __user *)arg; 7928c2ecf20Sopenharmony_ci int short_only, both; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci switch (cmd) { 7958c2ecf20Sopenharmony_ci case VFAT_IOCTL_READDIR_SHORT: 7968c2ecf20Sopenharmony_ci short_only = 1; 7978c2ecf20Sopenharmony_ci both = 0; 7988c2ecf20Sopenharmony_ci break; 7998c2ecf20Sopenharmony_ci case VFAT_IOCTL_READDIR_BOTH: 8008c2ecf20Sopenharmony_ci short_only = 0; 8018c2ecf20Sopenharmony_ci both = 1; 8028c2ecf20Sopenharmony_ci break; 8038c2ecf20Sopenharmony_ci default: 8048c2ecf20Sopenharmony_ci return fat_generic_ioctl(filp, cmd, arg); 8058c2ecf20Sopenharmony_ci } 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci /* 8088c2ecf20Sopenharmony_ci * Yes, we don't need this put_user() absolutely. However old 8098c2ecf20Sopenharmony_ci * code didn't return the right value. So, app use this value, 8108c2ecf20Sopenharmony_ci * in order to check whether it is EOF. 8118c2ecf20Sopenharmony_ci */ 8128c2ecf20Sopenharmony_ci if (put_user(0, &d1->d_reclen)) 8138c2ecf20Sopenharmony_ci return -EFAULT; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci return fat_ioctl_readdir(inode, filp, d1, fat_ioctl_filldir, 8168c2ecf20Sopenharmony_ci short_only, both); 8178c2ecf20Sopenharmony_ci} 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 8208c2ecf20Sopenharmony_ci#define VFAT_IOCTL_READDIR_BOTH32 _IOR('r', 1, struct compat_dirent[2]) 8218c2ecf20Sopenharmony_ci#define VFAT_IOCTL_READDIR_SHORT32 _IOR('r', 2, struct compat_dirent[2]) 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ciFAT_IOCTL_FILLDIR_FUNC(fat_compat_ioctl_filldir, compat_dirent) 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic long fat_compat_dir_ioctl(struct file *filp, unsigned cmd, 8268c2ecf20Sopenharmony_ci unsigned long arg) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci struct inode *inode = file_inode(filp); 8298c2ecf20Sopenharmony_ci struct compat_dirent __user *d1 = compat_ptr(arg); 8308c2ecf20Sopenharmony_ci int short_only, both; 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci switch (cmd) { 8338c2ecf20Sopenharmony_ci case VFAT_IOCTL_READDIR_SHORT32: 8348c2ecf20Sopenharmony_ci short_only = 1; 8358c2ecf20Sopenharmony_ci both = 0; 8368c2ecf20Sopenharmony_ci break; 8378c2ecf20Sopenharmony_ci case VFAT_IOCTL_READDIR_BOTH32: 8388c2ecf20Sopenharmony_ci short_only = 0; 8398c2ecf20Sopenharmony_ci both = 1; 8408c2ecf20Sopenharmony_ci break; 8418c2ecf20Sopenharmony_ci default: 8428c2ecf20Sopenharmony_ci return fat_generic_ioctl(filp, cmd, (unsigned long)arg); 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci /* 8468c2ecf20Sopenharmony_ci * Yes, we don't need this put_user() absolutely. However old 8478c2ecf20Sopenharmony_ci * code didn't return the right value. So, app use this value, 8488c2ecf20Sopenharmony_ci * in order to check whether it is EOF. 8498c2ecf20Sopenharmony_ci */ 8508c2ecf20Sopenharmony_ci if (put_user(0, &d1->d_reclen)) 8518c2ecf20Sopenharmony_ci return -EFAULT; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci return fat_ioctl_readdir(inode, filp, d1, fat_compat_ioctl_filldir, 8548c2ecf20Sopenharmony_ci short_only, both); 8558c2ecf20Sopenharmony_ci} 8568c2ecf20Sopenharmony_ci#endif /* CONFIG_COMPAT */ 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ciconst struct file_operations fat_dir_operations = { 8598c2ecf20Sopenharmony_ci .llseek = generic_file_llseek, 8608c2ecf20Sopenharmony_ci .read = generic_read_dir, 8618c2ecf20Sopenharmony_ci .iterate_shared = fat_readdir, 8628c2ecf20Sopenharmony_ci .unlocked_ioctl = fat_dir_ioctl, 8638c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 8648c2ecf20Sopenharmony_ci .compat_ioctl = fat_compat_dir_ioctl, 8658c2ecf20Sopenharmony_ci#endif 8668c2ecf20Sopenharmony_ci .fsync = fat_file_fsync, 8678c2ecf20Sopenharmony_ci}; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_cistatic int fat_get_short_entry(struct inode *dir, loff_t *pos, 8708c2ecf20Sopenharmony_ci struct buffer_head **bh, 8718c2ecf20Sopenharmony_ci struct msdos_dir_entry **de) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci while (fat_get_entry(dir, pos, bh, de) >= 0) { 8748c2ecf20Sopenharmony_ci /* free entry or long name entry or volume label */ 8758c2ecf20Sopenharmony_ci if (!IS_FREE((*de)->name) && !((*de)->attr & ATTR_VOLUME)) 8768c2ecf20Sopenharmony_ci return 0; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci return -ENOENT; 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci/* 8828c2ecf20Sopenharmony_ci * The ".." entry can not provide the "struct fat_slot_info" information 8838c2ecf20Sopenharmony_ci * for inode, nor a usable i_pos. So, this function provides some information 8848c2ecf20Sopenharmony_ci * only. 8858c2ecf20Sopenharmony_ci * 8868c2ecf20Sopenharmony_ci * Since this function walks through the on-disk inodes within a directory, 8878c2ecf20Sopenharmony_ci * callers are responsible for taking any locks necessary to prevent the 8888c2ecf20Sopenharmony_ci * directory from changing. 8898c2ecf20Sopenharmony_ci */ 8908c2ecf20Sopenharmony_ciint fat_get_dotdot_entry(struct inode *dir, struct buffer_head **bh, 8918c2ecf20Sopenharmony_ci struct msdos_dir_entry **de) 8928c2ecf20Sopenharmony_ci{ 8938c2ecf20Sopenharmony_ci loff_t offset = 0; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci *de = NULL; 8968c2ecf20Sopenharmony_ci while (fat_get_short_entry(dir, &offset, bh, de) >= 0) { 8978c2ecf20Sopenharmony_ci if (!strncmp((*de)->name, MSDOS_DOTDOT, MSDOS_NAME)) 8988c2ecf20Sopenharmony_ci return 0; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci return -ENOENT; 9018c2ecf20Sopenharmony_ci} 9028c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_get_dotdot_entry); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci/* See if directory is empty */ 9058c2ecf20Sopenharmony_ciint fat_dir_empty(struct inode *dir) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci struct buffer_head *bh; 9088c2ecf20Sopenharmony_ci struct msdos_dir_entry *de; 9098c2ecf20Sopenharmony_ci loff_t cpos; 9108c2ecf20Sopenharmony_ci int result = 0; 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ci bh = NULL; 9138c2ecf20Sopenharmony_ci cpos = 0; 9148c2ecf20Sopenharmony_ci while (fat_get_short_entry(dir, &cpos, &bh, &de) >= 0) { 9158c2ecf20Sopenharmony_ci if (strncmp(de->name, MSDOS_DOT , MSDOS_NAME) && 9168c2ecf20Sopenharmony_ci strncmp(de->name, MSDOS_DOTDOT, MSDOS_NAME)) { 9178c2ecf20Sopenharmony_ci result = -ENOTEMPTY; 9188c2ecf20Sopenharmony_ci break; 9198c2ecf20Sopenharmony_ci } 9208c2ecf20Sopenharmony_ci } 9218c2ecf20Sopenharmony_ci brelse(bh); 9228c2ecf20Sopenharmony_ci return result; 9238c2ecf20Sopenharmony_ci} 9248c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_dir_empty); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci/* 9278c2ecf20Sopenharmony_ci * fat_subdirs counts the number of sub-directories of dir. It can be run 9288c2ecf20Sopenharmony_ci * on directories being created. 9298c2ecf20Sopenharmony_ci */ 9308c2ecf20Sopenharmony_ciint fat_subdirs(struct inode *dir) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci struct buffer_head *bh; 9338c2ecf20Sopenharmony_ci struct msdos_dir_entry *de; 9348c2ecf20Sopenharmony_ci loff_t cpos; 9358c2ecf20Sopenharmony_ci int count = 0; 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci bh = NULL; 9388c2ecf20Sopenharmony_ci cpos = 0; 9398c2ecf20Sopenharmony_ci while (fat_get_short_entry(dir, &cpos, &bh, &de) >= 0) { 9408c2ecf20Sopenharmony_ci if (de->attr & ATTR_DIR) 9418c2ecf20Sopenharmony_ci count++; 9428c2ecf20Sopenharmony_ci } 9438c2ecf20Sopenharmony_ci brelse(bh); 9448c2ecf20Sopenharmony_ci return count; 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci/* 9488c2ecf20Sopenharmony_ci * Scans a directory for a given file (name points to its formatted name). 9498c2ecf20Sopenharmony_ci * Returns an error code or zero. 9508c2ecf20Sopenharmony_ci */ 9518c2ecf20Sopenharmony_ciint fat_scan(struct inode *dir, const unsigned char *name, 9528c2ecf20Sopenharmony_ci struct fat_slot_info *sinfo) 9538c2ecf20Sopenharmony_ci{ 9548c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci sinfo->slot_off = 0; 9578c2ecf20Sopenharmony_ci sinfo->bh = NULL; 9588c2ecf20Sopenharmony_ci while (fat_get_short_entry(dir, &sinfo->slot_off, &sinfo->bh, 9598c2ecf20Sopenharmony_ci &sinfo->de) >= 0) { 9608c2ecf20Sopenharmony_ci if (!strncmp(sinfo->de->name, name, MSDOS_NAME)) { 9618c2ecf20Sopenharmony_ci sinfo->slot_off -= sizeof(*sinfo->de); 9628c2ecf20Sopenharmony_ci sinfo->nr_slots = 1; 9638c2ecf20Sopenharmony_ci sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de); 9648c2ecf20Sopenharmony_ci return 0; 9658c2ecf20Sopenharmony_ci } 9668c2ecf20Sopenharmony_ci } 9678c2ecf20Sopenharmony_ci return -ENOENT; 9688c2ecf20Sopenharmony_ci} 9698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_scan); 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci/* 9728c2ecf20Sopenharmony_ci * Scans a directory for a given logstart. 9738c2ecf20Sopenharmony_ci * Returns an error code or zero. 9748c2ecf20Sopenharmony_ci */ 9758c2ecf20Sopenharmony_ciint fat_scan_logstart(struct inode *dir, int i_logstart, 9768c2ecf20Sopenharmony_ci struct fat_slot_info *sinfo) 9778c2ecf20Sopenharmony_ci{ 9788c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci sinfo->slot_off = 0; 9818c2ecf20Sopenharmony_ci sinfo->bh = NULL; 9828c2ecf20Sopenharmony_ci while (fat_get_short_entry(dir, &sinfo->slot_off, &sinfo->bh, 9838c2ecf20Sopenharmony_ci &sinfo->de) >= 0) { 9848c2ecf20Sopenharmony_ci if (fat_get_start(MSDOS_SB(sb), sinfo->de) == i_logstart) { 9858c2ecf20Sopenharmony_ci sinfo->slot_off -= sizeof(*sinfo->de); 9868c2ecf20Sopenharmony_ci sinfo->nr_slots = 1; 9878c2ecf20Sopenharmony_ci sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de); 9888c2ecf20Sopenharmony_ci return 0; 9898c2ecf20Sopenharmony_ci } 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci return -ENOENT; 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic int __fat_remove_entries(struct inode *dir, loff_t pos, int nr_slots) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 9978c2ecf20Sopenharmony_ci struct buffer_head *bh; 9988c2ecf20Sopenharmony_ci struct msdos_dir_entry *de, *endp; 9998c2ecf20Sopenharmony_ci int err = 0, orig_slots; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci while (nr_slots) { 10028c2ecf20Sopenharmony_ci bh = NULL; 10038c2ecf20Sopenharmony_ci if (fat_get_entry(dir, &pos, &bh, &de) < 0) { 10048c2ecf20Sopenharmony_ci err = -EIO; 10058c2ecf20Sopenharmony_ci break; 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci orig_slots = nr_slots; 10098c2ecf20Sopenharmony_ci endp = (struct msdos_dir_entry *)(bh->b_data + sb->s_blocksize); 10108c2ecf20Sopenharmony_ci while (nr_slots && de < endp) { 10118c2ecf20Sopenharmony_ci de->name[0] = DELETED_FLAG; 10128c2ecf20Sopenharmony_ci de++; 10138c2ecf20Sopenharmony_ci nr_slots--; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci mark_buffer_dirty_inode(bh, dir); 10168c2ecf20Sopenharmony_ci if (IS_DIRSYNC(dir)) 10178c2ecf20Sopenharmony_ci err = sync_dirty_buffer(bh); 10188c2ecf20Sopenharmony_ci brelse(bh); 10198c2ecf20Sopenharmony_ci if (err) 10208c2ecf20Sopenharmony_ci break; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci /* pos is *next* de's position, so this does `- sizeof(de)' */ 10238c2ecf20Sopenharmony_ci pos += ((orig_slots - nr_slots) * sizeof(*de)) - sizeof(*de); 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci return err; 10278c2ecf20Sopenharmony_ci} 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ciint fat_remove_entries(struct inode *dir, struct fat_slot_info *sinfo) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 10328c2ecf20Sopenharmony_ci struct msdos_dir_entry *de; 10338c2ecf20Sopenharmony_ci struct buffer_head *bh; 10348c2ecf20Sopenharmony_ci int err = 0, nr_slots; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci /* 10378c2ecf20Sopenharmony_ci * First stage: Remove the shortname. By this, the directory 10388c2ecf20Sopenharmony_ci * entry is removed. 10398c2ecf20Sopenharmony_ci */ 10408c2ecf20Sopenharmony_ci nr_slots = sinfo->nr_slots; 10418c2ecf20Sopenharmony_ci de = sinfo->de; 10428c2ecf20Sopenharmony_ci sinfo->de = NULL; 10438c2ecf20Sopenharmony_ci bh = sinfo->bh; 10448c2ecf20Sopenharmony_ci sinfo->bh = NULL; 10458c2ecf20Sopenharmony_ci while (nr_slots && de >= (struct msdos_dir_entry *)bh->b_data) { 10468c2ecf20Sopenharmony_ci de->name[0] = DELETED_FLAG; 10478c2ecf20Sopenharmony_ci de--; 10488c2ecf20Sopenharmony_ci nr_slots--; 10498c2ecf20Sopenharmony_ci } 10508c2ecf20Sopenharmony_ci mark_buffer_dirty_inode(bh, dir); 10518c2ecf20Sopenharmony_ci if (IS_DIRSYNC(dir)) 10528c2ecf20Sopenharmony_ci err = sync_dirty_buffer(bh); 10538c2ecf20Sopenharmony_ci brelse(bh); 10548c2ecf20Sopenharmony_ci if (err) 10558c2ecf20Sopenharmony_ci return err; 10568c2ecf20Sopenharmony_ci inode_inc_iversion(dir); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (nr_slots) { 10598c2ecf20Sopenharmony_ci /* 10608c2ecf20Sopenharmony_ci * Second stage: remove the remaining longname slots. 10618c2ecf20Sopenharmony_ci * (This directory entry is already removed, and so return 10628c2ecf20Sopenharmony_ci * the success) 10638c2ecf20Sopenharmony_ci */ 10648c2ecf20Sopenharmony_ci err = __fat_remove_entries(dir, sinfo->slot_off, nr_slots); 10658c2ecf20Sopenharmony_ci if (err) { 10668c2ecf20Sopenharmony_ci fat_msg(sb, KERN_WARNING, 10678c2ecf20Sopenharmony_ci "Couldn't remove the long name slots"); 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci } 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci fat_truncate_time(dir, NULL, S_ATIME|S_MTIME); 10728c2ecf20Sopenharmony_ci if (IS_DIRSYNC(dir)) 10738c2ecf20Sopenharmony_ci (void)fat_sync_inode(dir); 10748c2ecf20Sopenharmony_ci else 10758c2ecf20Sopenharmony_ci mark_inode_dirty(dir); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci return 0; 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_remove_entries); 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_cistatic int fat_zeroed_cluster(struct inode *dir, sector_t blknr, int nr_used, 10828c2ecf20Sopenharmony_ci struct buffer_head **bhs, int nr_bhs) 10838c2ecf20Sopenharmony_ci{ 10848c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 10858c2ecf20Sopenharmony_ci sector_t last_blknr = blknr + MSDOS_SB(sb)->sec_per_clus; 10868c2ecf20Sopenharmony_ci int err, i, n; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci /* Zeroing the unused blocks on this cluster */ 10898c2ecf20Sopenharmony_ci blknr += nr_used; 10908c2ecf20Sopenharmony_ci n = nr_used; 10918c2ecf20Sopenharmony_ci while (blknr < last_blknr) { 10928c2ecf20Sopenharmony_ci bhs[n] = sb_getblk(sb, blknr); 10938c2ecf20Sopenharmony_ci if (!bhs[n]) { 10948c2ecf20Sopenharmony_ci err = -ENOMEM; 10958c2ecf20Sopenharmony_ci goto error; 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci /* Avoid race with userspace read via bdev */ 10988c2ecf20Sopenharmony_ci lock_buffer(bhs[n]); 10998c2ecf20Sopenharmony_ci memset(bhs[n]->b_data, 0, sb->s_blocksize); 11008c2ecf20Sopenharmony_ci set_buffer_uptodate(bhs[n]); 11018c2ecf20Sopenharmony_ci unlock_buffer(bhs[n]); 11028c2ecf20Sopenharmony_ci mark_buffer_dirty_inode(bhs[n], dir); 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_ci n++; 11058c2ecf20Sopenharmony_ci blknr++; 11068c2ecf20Sopenharmony_ci if (n == nr_bhs) { 11078c2ecf20Sopenharmony_ci if (IS_DIRSYNC(dir)) { 11088c2ecf20Sopenharmony_ci err = fat_sync_bhs(bhs, n); 11098c2ecf20Sopenharmony_ci if (err) 11108c2ecf20Sopenharmony_ci goto error; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) 11138c2ecf20Sopenharmony_ci brelse(bhs[i]); 11148c2ecf20Sopenharmony_ci n = 0; 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci } 11178c2ecf20Sopenharmony_ci if (IS_DIRSYNC(dir)) { 11188c2ecf20Sopenharmony_ci err = fat_sync_bhs(bhs, n); 11198c2ecf20Sopenharmony_ci if (err) 11208c2ecf20Sopenharmony_ci goto error; 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) 11238c2ecf20Sopenharmony_ci brelse(bhs[i]); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci return 0; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_cierror: 11288c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) 11298c2ecf20Sopenharmony_ci bforget(bhs[i]); 11308c2ecf20Sopenharmony_ci return err; 11318c2ecf20Sopenharmony_ci} 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ciint fat_alloc_new_dir(struct inode *dir, struct timespec64 *ts) 11348c2ecf20Sopenharmony_ci{ 11358c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 11368c2ecf20Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 11378c2ecf20Sopenharmony_ci struct buffer_head *bhs[MAX_BUF_PER_PAGE]; 11388c2ecf20Sopenharmony_ci struct msdos_dir_entry *de; 11398c2ecf20Sopenharmony_ci sector_t blknr; 11408c2ecf20Sopenharmony_ci __le16 date, time; 11418c2ecf20Sopenharmony_ci u8 time_cs; 11428c2ecf20Sopenharmony_ci int err, cluster; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci err = fat_alloc_clusters(dir, &cluster, 1); 11458c2ecf20Sopenharmony_ci if (err) 11468c2ecf20Sopenharmony_ci goto error; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci blknr = fat_clus_to_blknr(sbi, cluster); 11498c2ecf20Sopenharmony_ci bhs[0] = sb_getblk(sb, blknr); 11508c2ecf20Sopenharmony_ci if (!bhs[0]) { 11518c2ecf20Sopenharmony_ci err = -ENOMEM; 11528c2ecf20Sopenharmony_ci goto error_free; 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci fat_time_unix2fat(sbi, ts, &time, &date, &time_cs); 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci de = (struct msdos_dir_entry *)bhs[0]->b_data; 11588c2ecf20Sopenharmony_ci /* Avoid race with userspace read via bdev */ 11598c2ecf20Sopenharmony_ci lock_buffer(bhs[0]); 11608c2ecf20Sopenharmony_ci /* filling the new directory slots ("." and ".." entries) */ 11618c2ecf20Sopenharmony_ci memcpy(de[0].name, MSDOS_DOT, MSDOS_NAME); 11628c2ecf20Sopenharmony_ci memcpy(de[1].name, MSDOS_DOTDOT, MSDOS_NAME); 11638c2ecf20Sopenharmony_ci de->attr = de[1].attr = ATTR_DIR; 11648c2ecf20Sopenharmony_ci de[0].lcase = de[1].lcase = 0; 11658c2ecf20Sopenharmony_ci de[0].time = de[1].time = time; 11668c2ecf20Sopenharmony_ci de[0].date = de[1].date = date; 11678c2ecf20Sopenharmony_ci if (sbi->options.isvfat) { 11688c2ecf20Sopenharmony_ci /* extra timestamps */ 11698c2ecf20Sopenharmony_ci de[0].ctime = de[1].ctime = time; 11708c2ecf20Sopenharmony_ci de[0].ctime_cs = de[1].ctime_cs = time_cs; 11718c2ecf20Sopenharmony_ci de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = date; 11728c2ecf20Sopenharmony_ci } else { 11738c2ecf20Sopenharmony_ci de[0].ctime = de[1].ctime = 0; 11748c2ecf20Sopenharmony_ci de[0].ctime_cs = de[1].ctime_cs = 0; 11758c2ecf20Sopenharmony_ci de[0].adate = de[0].cdate = de[1].adate = de[1].cdate = 0; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci fat_set_start(&de[0], cluster); 11788c2ecf20Sopenharmony_ci fat_set_start(&de[1], MSDOS_I(dir)->i_logstart); 11798c2ecf20Sopenharmony_ci de[0].size = de[1].size = 0; 11808c2ecf20Sopenharmony_ci memset(de + 2, 0, sb->s_blocksize - 2 * sizeof(*de)); 11818c2ecf20Sopenharmony_ci set_buffer_uptodate(bhs[0]); 11828c2ecf20Sopenharmony_ci unlock_buffer(bhs[0]); 11838c2ecf20Sopenharmony_ci mark_buffer_dirty_inode(bhs[0], dir); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci err = fat_zeroed_cluster(dir, blknr, 1, bhs, MAX_BUF_PER_PAGE); 11868c2ecf20Sopenharmony_ci if (err) 11878c2ecf20Sopenharmony_ci goto error_free; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci return cluster; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_cierror_free: 11928c2ecf20Sopenharmony_ci fat_free_clusters(dir, cluster); 11938c2ecf20Sopenharmony_cierror: 11948c2ecf20Sopenharmony_ci return err; 11958c2ecf20Sopenharmony_ci} 11968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_alloc_new_dir); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_cistatic int fat_add_new_entries(struct inode *dir, void *slots, int nr_slots, 11998c2ecf20Sopenharmony_ci int *nr_cluster, struct msdos_dir_entry **de, 12008c2ecf20Sopenharmony_ci struct buffer_head **bh, loff_t *i_pos) 12018c2ecf20Sopenharmony_ci{ 12028c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 12038c2ecf20Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 12048c2ecf20Sopenharmony_ci struct buffer_head *bhs[MAX_BUF_PER_PAGE]; 12058c2ecf20Sopenharmony_ci sector_t blknr, start_blknr, last_blknr; 12068c2ecf20Sopenharmony_ci unsigned long size, copy; 12078c2ecf20Sopenharmony_ci int err, i, n, offset, cluster[2]; 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci /* 12108c2ecf20Sopenharmony_ci * The minimum cluster size is 512bytes, and maximum entry 12118c2ecf20Sopenharmony_ci * size is 32*slots (672bytes). So, iff the cluster size is 12128c2ecf20Sopenharmony_ci * 512bytes, we may need two clusters. 12138c2ecf20Sopenharmony_ci */ 12148c2ecf20Sopenharmony_ci size = nr_slots * sizeof(struct msdos_dir_entry); 12158c2ecf20Sopenharmony_ci *nr_cluster = (size + (sbi->cluster_size - 1)) >> sbi->cluster_bits; 12168c2ecf20Sopenharmony_ci BUG_ON(*nr_cluster > 2); 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci err = fat_alloc_clusters(dir, cluster, *nr_cluster); 12198c2ecf20Sopenharmony_ci if (err) 12208c2ecf20Sopenharmony_ci goto error; 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci /* 12238c2ecf20Sopenharmony_ci * First stage: Fill the directory entry. NOTE: This cluster 12248c2ecf20Sopenharmony_ci * is not referenced from any inode yet, so updates order is 12258c2ecf20Sopenharmony_ci * not important. 12268c2ecf20Sopenharmony_ci */ 12278c2ecf20Sopenharmony_ci i = n = copy = 0; 12288c2ecf20Sopenharmony_ci do { 12298c2ecf20Sopenharmony_ci start_blknr = blknr = fat_clus_to_blknr(sbi, cluster[i]); 12308c2ecf20Sopenharmony_ci last_blknr = start_blknr + sbi->sec_per_clus; 12318c2ecf20Sopenharmony_ci while (blknr < last_blknr) { 12328c2ecf20Sopenharmony_ci bhs[n] = sb_getblk(sb, blknr); 12338c2ecf20Sopenharmony_ci if (!bhs[n]) { 12348c2ecf20Sopenharmony_ci err = -ENOMEM; 12358c2ecf20Sopenharmony_ci goto error_nomem; 12368c2ecf20Sopenharmony_ci } 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci /* fill the directory entry */ 12398c2ecf20Sopenharmony_ci copy = min(size, sb->s_blocksize); 12408c2ecf20Sopenharmony_ci /* Avoid race with userspace read via bdev */ 12418c2ecf20Sopenharmony_ci lock_buffer(bhs[n]); 12428c2ecf20Sopenharmony_ci memcpy(bhs[n]->b_data, slots, copy); 12438c2ecf20Sopenharmony_ci set_buffer_uptodate(bhs[n]); 12448c2ecf20Sopenharmony_ci unlock_buffer(bhs[n]); 12458c2ecf20Sopenharmony_ci mark_buffer_dirty_inode(bhs[n], dir); 12468c2ecf20Sopenharmony_ci slots += copy; 12478c2ecf20Sopenharmony_ci size -= copy; 12488c2ecf20Sopenharmony_ci if (!size) 12498c2ecf20Sopenharmony_ci break; 12508c2ecf20Sopenharmony_ci n++; 12518c2ecf20Sopenharmony_ci blknr++; 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci } while (++i < *nr_cluster); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci memset(bhs[n]->b_data + copy, 0, sb->s_blocksize - copy); 12568c2ecf20Sopenharmony_ci offset = copy - sizeof(struct msdos_dir_entry); 12578c2ecf20Sopenharmony_ci get_bh(bhs[n]); 12588c2ecf20Sopenharmony_ci *bh = bhs[n]; 12598c2ecf20Sopenharmony_ci *de = (struct msdos_dir_entry *)((*bh)->b_data + offset); 12608c2ecf20Sopenharmony_ci *i_pos = fat_make_i_pos(sb, *bh, *de); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci /* Second stage: clear the rest of cluster, and write outs */ 12638c2ecf20Sopenharmony_ci err = fat_zeroed_cluster(dir, start_blknr, ++n, bhs, MAX_BUF_PER_PAGE); 12648c2ecf20Sopenharmony_ci if (err) 12658c2ecf20Sopenharmony_ci goto error_free; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_ci return cluster[0]; 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_cierror_free: 12708c2ecf20Sopenharmony_ci brelse(*bh); 12718c2ecf20Sopenharmony_ci *bh = NULL; 12728c2ecf20Sopenharmony_ci n = 0; 12738c2ecf20Sopenharmony_cierror_nomem: 12748c2ecf20Sopenharmony_ci for (i = 0; i < n; i++) 12758c2ecf20Sopenharmony_ci bforget(bhs[i]); 12768c2ecf20Sopenharmony_ci fat_free_clusters(dir, cluster[0]); 12778c2ecf20Sopenharmony_cierror: 12788c2ecf20Sopenharmony_ci return err; 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ciint fat_add_entries(struct inode *dir, void *slots, int nr_slots, 12828c2ecf20Sopenharmony_ci struct fat_slot_info *sinfo) 12838c2ecf20Sopenharmony_ci{ 12848c2ecf20Sopenharmony_ci struct super_block *sb = dir->i_sb; 12858c2ecf20Sopenharmony_ci struct msdos_sb_info *sbi = MSDOS_SB(sb); 12868c2ecf20Sopenharmony_ci struct buffer_head *bh, *prev, *bhs[3]; /* 32*slots (672bytes) */ 12878c2ecf20Sopenharmony_ci struct msdos_dir_entry *de; 12888c2ecf20Sopenharmony_ci int err, free_slots, i, nr_bhs; 12898c2ecf20Sopenharmony_ci loff_t pos, i_pos; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci sinfo->nr_slots = nr_slots; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci /* First stage: search free directory entries */ 12948c2ecf20Sopenharmony_ci free_slots = nr_bhs = 0; 12958c2ecf20Sopenharmony_ci bh = prev = NULL; 12968c2ecf20Sopenharmony_ci pos = 0; 12978c2ecf20Sopenharmony_ci err = -ENOSPC; 12988c2ecf20Sopenharmony_ci while (fat_get_entry(dir, &pos, &bh, &de) > -1) { 12998c2ecf20Sopenharmony_ci /* check the maximum size of directory */ 13008c2ecf20Sopenharmony_ci if (pos >= FAT_MAX_DIR_SIZE) 13018c2ecf20Sopenharmony_ci goto error; 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci if (IS_FREE(de->name)) { 13048c2ecf20Sopenharmony_ci if (prev != bh) { 13058c2ecf20Sopenharmony_ci get_bh(bh); 13068c2ecf20Sopenharmony_ci bhs[nr_bhs] = prev = bh; 13078c2ecf20Sopenharmony_ci nr_bhs++; 13088c2ecf20Sopenharmony_ci } 13098c2ecf20Sopenharmony_ci free_slots++; 13108c2ecf20Sopenharmony_ci if (free_slots == nr_slots) 13118c2ecf20Sopenharmony_ci goto found; 13128c2ecf20Sopenharmony_ci } else { 13138c2ecf20Sopenharmony_ci for (i = 0; i < nr_bhs; i++) 13148c2ecf20Sopenharmony_ci brelse(bhs[i]); 13158c2ecf20Sopenharmony_ci prev = NULL; 13168c2ecf20Sopenharmony_ci free_slots = nr_bhs = 0; 13178c2ecf20Sopenharmony_ci } 13188c2ecf20Sopenharmony_ci } 13198c2ecf20Sopenharmony_ci if (dir->i_ino == MSDOS_ROOT_INO) { 13208c2ecf20Sopenharmony_ci if (!is_fat32(sbi)) 13218c2ecf20Sopenharmony_ci goto error; 13228c2ecf20Sopenharmony_ci } else if (MSDOS_I(dir)->i_start == 0) { 13238c2ecf20Sopenharmony_ci fat_msg(sb, KERN_ERR, "Corrupted directory (i_pos %lld)", 13248c2ecf20Sopenharmony_ci MSDOS_I(dir)->i_pos); 13258c2ecf20Sopenharmony_ci err = -EIO; 13268c2ecf20Sopenharmony_ci goto error; 13278c2ecf20Sopenharmony_ci } 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_cifound: 13308c2ecf20Sopenharmony_ci err = 0; 13318c2ecf20Sopenharmony_ci pos -= free_slots * sizeof(*de); 13328c2ecf20Sopenharmony_ci nr_slots -= free_slots; 13338c2ecf20Sopenharmony_ci if (free_slots) { 13348c2ecf20Sopenharmony_ci /* 13358c2ecf20Sopenharmony_ci * Second stage: filling the free entries with new entries. 13368c2ecf20Sopenharmony_ci * NOTE: If this slots has shortname, first, we write 13378c2ecf20Sopenharmony_ci * the long name slots, then write the short name. 13388c2ecf20Sopenharmony_ci */ 13398c2ecf20Sopenharmony_ci int size = free_slots * sizeof(*de); 13408c2ecf20Sopenharmony_ci int offset = pos & (sb->s_blocksize - 1); 13418c2ecf20Sopenharmony_ci int long_bhs = nr_bhs - (nr_slots == 0); 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci /* Fill the long name slots. */ 13448c2ecf20Sopenharmony_ci for (i = 0; i < long_bhs; i++) { 13458c2ecf20Sopenharmony_ci int copy = min_t(int, sb->s_blocksize - offset, size); 13468c2ecf20Sopenharmony_ci memcpy(bhs[i]->b_data + offset, slots, copy); 13478c2ecf20Sopenharmony_ci mark_buffer_dirty_inode(bhs[i], dir); 13488c2ecf20Sopenharmony_ci offset = 0; 13498c2ecf20Sopenharmony_ci slots += copy; 13508c2ecf20Sopenharmony_ci size -= copy; 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci if (long_bhs && IS_DIRSYNC(dir)) 13538c2ecf20Sopenharmony_ci err = fat_sync_bhs(bhs, long_bhs); 13548c2ecf20Sopenharmony_ci if (!err && i < nr_bhs) { 13558c2ecf20Sopenharmony_ci /* Fill the short name slot. */ 13568c2ecf20Sopenharmony_ci int copy = min_t(int, sb->s_blocksize - offset, size); 13578c2ecf20Sopenharmony_ci memcpy(bhs[i]->b_data + offset, slots, copy); 13588c2ecf20Sopenharmony_ci mark_buffer_dirty_inode(bhs[i], dir); 13598c2ecf20Sopenharmony_ci if (IS_DIRSYNC(dir)) 13608c2ecf20Sopenharmony_ci err = sync_dirty_buffer(bhs[i]); 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci for (i = 0; i < nr_bhs; i++) 13638c2ecf20Sopenharmony_ci brelse(bhs[i]); 13648c2ecf20Sopenharmony_ci if (err) 13658c2ecf20Sopenharmony_ci goto error_remove; 13668c2ecf20Sopenharmony_ci } 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci if (nr_slots) { 13698c2ecf20Sopenharmony_ci int cluster, nr_cluster; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci /* 13728c2ecf20Sopenharmony_ci * Third stage: allocate the cluster for new entries. 13738c2ecf20Sopenharmony_ci * And initialize the cluster with new entries, then 13748c2ecf20Sopenharmony_ci * add the cluster to dir. 13758c2ecf20Sopenharmony_ci */ 13768c2ecf20Sopenharmony_ci cluster = fat_add_new_entries(dir, slots, nr_slots, &nr_cluster, 13778c2ecf20Sopenharmony_ci &de, &bh, &i_pos); 13788c2ecf20Sopenharmony_ci if (cluster < 0) { 13798c2ecf20Sopenharmony_ci err = cluster; 13808c2ecf20Sopenharmony_ci goto error_remove; 13818c2ecf20Sopenharmony_ci } 13828c2ecf20Sopenharmony_ci err = fat_chain_add(dir, cluster, nr_cluster); 13838c2ecf20Sopenharmony_ci if (err) { 13848c2ecf20Sopenharmony_ci fat_free_clusters(dir, cluster); 13858c2ecf20Sopenharmony_ci goto error_remove; 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci if (dir->i_size & (sbi->cluster_size - 1)) { 13888c2ecf20Sopenharmony_ci fat_fs_error(sb, "Odd directory size"); 13898c2ecf20Sopenharmony_ci dir->i_size = (dir->i_size + sbi->cluster_size - 1) 13908c2ecf20Sopenharmony_ci & ~((loff_t)sbi->cluster_size - 1); 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci dir->i_size += nr_cluster << sbi->cluster_bits; 13938c2ecf20Sopenharmony_ci MSDOS_I(dir)->mmu_private += nr_cluster << sbi->cluster_bits; 13948c2ecf20Sopenharmony_ci } 13958c2ecf20Sopenharmony_ci sinfo->slot_off = pos; 13968c2ecf20Sopenharmony_ci sinfo->de = de; 13978c2ecf20Sopenharmony_ci sinfo->bh = bh; 13988c2ecf20Sopenharmony_ci sinfo->i_pos = fat_make_i_pos(sb, sinfo->bh, sinfo->de); 13998c2ecf20Sopenharmony_ci 14008c2ecf20Sopenharmony_ci return 0; 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_cierror: 14038c2ecf20Sopenharmony_ci brelse(bh); 14048c2ecf20Sopenharmony_ci for (i = 0; i < nr_bhs; i++) 14058c2ecf20Sopenharmony_ci brelse(bhs[i]); 14068c2ecf20Sopenharmony_ci return err; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_cierror_remove: 14098c2ecf20Sopenharmony_ci brelse(bh); 14108c2ecf20Sopenharmony_ci if (free_slots) 14118c2ecf20Sopenharmony_ci __fat_remove_entries(dir, pos, free_slots); 14128c2ecf20Sopenharmony_ci return err; 14138c2ecf20Sopenharmony_ci} 14148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(fat_add_entries); 1415