* refs/heads/tmp-0ca3fca
Linux 4.4.163
x86/time: Correct the attribute on jiffies' definition
l2tp: hold tunnel socket when handling control frames in l2tp_ip and l2tp_ip6
cpuidle: Do not access cpuidle_devices when !CONFIG_CPU_IDLE
x86/percpu: Fix this_cpu_read()
sched/fair: Fix throttle_list starvation with low CFS quota
Input: elan_i2c - add ACPI ID for Lenovo IdeaPad 330-15IGM
USB: fix the usbfs flag sanitization for control transfers
usb: gadget: storage: Fix Spectre v1 vulnerability
cdc-acm: correct counting of UART states in serial state notification
IB/ucm: Fix Spectre v1 vulnerability
RDMA/ucma: Fix Spectre v1 vulnerability
ptp: fix Spectre v1 vulnerability
cachefiles: fix the race between cachefiles_bury_object() and rmdir(2)
ahci: don't ignore result code of ahci_reset_controller()
crypto: shash - Fix a sleep-in-atomic bug in shash_setkey_unaligned
mremap: properly flush TLB before releasing the page
rtnetlink: Disallow FDB configuration for non-Ethernet device
vhost: Fix Spectre V1 vulnerability
net: drop skb on failure in ip_check_defrag()
sctp: fix race on sctp_id2asoc
r8169: fix NAPI handling under high load
net: stmmac: Fix stmmac_mdio_reset() when building stmmac as modules
net: socket: fix a missing-check bug
net: sched: gred: pass the right attribute to gred_change_table_def()
net/ipv6: Fix index counter for unicast addresses in in6_dump_addrs
ipv6/ndisc: Preserve IPv6 control buffer if protocol error handlers are called
ipv6: mcast: fix a use-after-free in inet6_mc_check
net: bridge: remove ipv6 zero address check in mcast queries
bridge: do not add port to router list when receives query with source 0.0.0.0
perf tools: Disable parallelism for 'make clean'
mtd: spi-nor: Add support for is25wp series chips
fs/fat/fatent.c: add cond_resched() to fat_count_free_clusters()
ARM: dts: imx53-qsb: disable 1.2GHz OPP
MIPS: DEC: Fix an int-handler.S CPU_DADDI_WORKAROUNDS regression
igb: Remove superfluous reset to PHY and page 0 selection
MIPS: microMIPS: Fix decoding of swsp16 instruction
scsi: aacraid: Fix typo in blink status
bonding: avoid defaulting hard_header_len to ETH_HLEN on slave removal
PM / devfreq: tegra: fix error return code in tegra_devfreq_probe()
ASoC: spear: fix error return code in spdif_in_probe()
spi: xlp: fix error return code in xlp_spi_probe()
spi/bcm63xx: fix error return code in bcm63xx_spi_probe()
MIPS: Handle non word sized instructions when examining frame
spi/bcm63xx-hspi: fix error return code in bcm63xx_hsspi_probe()
usb: dwc3: omap: fix error return code in dwc3_omap_probe()
usb: ehci-omap: fix error return code in ehci_hcd_omap_probe()
usb: imx21-hcd: fix error return code in imx21_probe()
gpio: msic: fix error return code in platform_msic_gpio_probe()
sparc64: Fix exception handling in UltraSPARC-III memcpy.
gpu: host1x: fix error return code in host1x_probe()
sparc64 mm: Fix more TSB sizing issues
video: fbdev: pxa3xx_gcu: fix error return code in pxa3xx_gcu_probe()
tty: serial: sprd: fix error return code in sprd_probe()
l2tp: hold socket before dropping lock in l2tp_ip{, 6}_recv()
brcmfmac: Fix glom_skb leak in brcmf_sdiod_recv_chain
gro: Allow tunnel stacking in the case of FOU/GUE
vti6: flush x-netns xfrm cache when vti interface is removed
ALSA: timer: Fix zero-division by continue of uninitialized instance
ixgbe: Correct X550EM_x revision check
ixgbe: fix RSS limit for X550
net/mlx5e: Correctly handle RSS indirection table when changing number of channels
net/mlx5e: Fix LRO modify
ixgbevf: Fix handling of NAPI budget when multiple queues are enabled per vector
fuse: Dont call set_page_dirty_lock() for ITER_BVEC pages for async_dio
drm/nouveau/fbcon: fix oops without fbdev emulation
bpf: generally move prog destruction to RCU deferral
usb-storage: fix bogus hardware error messages for ATA pass-thru devices
sch_red: update backlog as well
sparc/pci: Refactor dev_archdata initialization into pci_init_dev_archdata
scsi: Add STARGET_CREATED_REMOVE state to scsi_target_state
xfrm: Clear sk_dst_cache when applying per-socket policy.
arm64: Fix potential race with hardware DBM in ptep_set_access_flags()
CIFS: handle guest access errors to Windows shares
ASoC: wm8940: Enable cache usage to fix crashes on resume
ASoC: ak4613: Enable cache usage to fix crashes on resume
MIPS: Fix FCSR Cause bit handling for correct SIGFPE issue
usbvision: revert commit 588afcc1
perf/core: Don't leak event in the syscall error path
aacraid: Start adapter after updating number of MSIX vectors
x86/PCI: Mark Broadwell-EP Home Agent 1 as having non-compliant BARs
tpm: fix: return rc when devm_add_action() fails
thermal: allow u8500-thermal driver to be a module
thermal: allow spear-thermal driver to be a module
btrfs: don't create or leak aliased root while cleaning up orphans
sched/cgroup: Fix cgroup entity load tracking tear-down
um: Avoid longjmp/setjmp symbol clashes with libpthread.a
ipv6: orphan skbs in reassembly unit
net/mlx4_en: Resolve dividing by zero in 32-bit system
af_iucv: Move sockaddr length checks to before accessing sa_family in bind and connect handlers
radix-tree: fix radix_tree_iter_retry() for tagged iterators.
x86/mm/pat: Prevent hang during boot when mapping pages
ARM: dts: apq8064: add ahci ports-implemented mask
tracing: Skip more functions when doing stack tracing of events
ser_gigaset: use container_of() instead of detour
net: drop write-only stack variable
ipv6: suppress sparse warnings in IP6_ECN_set_ce()
KEYS: put keyring if install_session_keyring_to_cred() fails
net: cxgb3_main: fix a missing-check bug
perf/ring_buffer: Prevent concurent ring buffer access
smsc95xx: Check for Wake-on-LAN modes
smsc75xx: Check for Wake-on-LAN modes
r8152: Check for supported Wake-on-LAN Modes
sr9800: Check for supported Wake-on-LAN modes
lan78xx: Check for supported Wake-on-LAN modes
ax88179_178a: Check for supported Wake-on-LAN modes
asix: Check for supported Wake-on-LAN modes
pxa168fb: prepare the clock
Bluetooth: SMP: fix crash in unpairing
mac80211_hwsim: do not omit multicast announce of first added radio
xfrm: validate template mode
ARM: 8799/1: mm: fix pci_ioremap_io() offset check
cfg80211: reg: Init wiphy_idx in regulatory_hint_core()
mac80211: Always report TX status
xfrm6: call kfree_skb when skb is toobig
xfrm: Validate address prefix lengths in the xfrm selector.
BACKPORT: xfrm: Allow Output Mark to be Updated Using UPDSA
ANDROID: sdcardfs: Add option to drop unused dentries
f2fs: guarantee journalled quota data by checkpoint
f2fs: cleanup dirty pages if recover failed
f2fs: fix data corruption issue with hardware encryption
f2fs: fix to recover inode->i_flags of inode block during POR
f2fs: spread f2fs_set_inode_flags()
f2fs: fix to spread clear_cold_data()
Revert "f2fs: fix to clear PG_checked flag in set_page_dirty()"
f2fs: account read IOs and use IO counts for is_idle
f2fs: fix to account IO correctly for cgroup writeback
f2fs: fix to account IO correctly
f2fs: remove request_list check in is_idle()
f2fs: allow to mount, if quota is failed
f2fs: update REQ_TIME in f2fs_cross_rename()
f2fs: do not update REQ_TIME in case of error conditions
f2fs: remove unneeded disable_nat_bits()
f2fs: remove unused sbi->trigger_ssr_threshold
f2fs: shrink sbi->sb_lock coverage in set_file_temperature()
f2fs: fix to recover cold bit of inode block during POR
f2fs: submit cached bio to avoid endless PageWriteback
f2fs: checkpoint disabling
f2fs: clear PageError on the read path
f2fs: allow out-place-update for direct IO in LFS mode
f2fs: refactor ->page_mkwrite() flow
Revert: "f2fs: check last page index in cached bio to decide submission"
f2fs: support superblock checksum
f2fs: add to account skip count of background GC
f2fs: add to account meta IO
f2fs: keep lazytime on remount
f2fs: fix missing up_read
f2fs: return correct errno in f2fs_gc
f2fs: avoid f2fs_bug_on if f2fs_get_meta_page_nofail got EIO
f2fs: mark inode dirty explicitly in recover_inode()
f2fs: fix to recover inode's crtime during POR
f2fs: fix to recover inode's i_gc_failures during POR
f2fs: fix to recover inode's i_flags during POR
f2fs: fix to recover inode's project id during POR
f2fs: update i_size after DIO completion
f2fs: report ENOENT correctly in f2fs_rename
f2fs: fix remount problem of option io_bits
f2fs: fix to recover inode's uid/gid during POR
f2fs: avoid infinite loop in f2fs_alloc_nid
f2fs: add new idle interval timing for discard and gc paths
f2fs: split IO error injection according to RW
f2fs: add SPDX license identifiers
f2fs: surround fault_injection related option parsing using CONFIG_F2FS_FAULT_INJECTION
f2fs: avoid sleeping under spin_lock
f2fs: plug readahead IO in readdir()
f2fs: fix to do sanity check with current segment number
f2fs: fix memory leak of percpu counter in fill_super()
f2fs: fix memory leak of write_io in fill_super()
f2fs: cache NULL when both default_acl and acl are NULL
f2fs: fix to flush all dirty inodes recovered in readonly fs
f2fs: report error if quota off error during umount
f2fs: submit bio after shutdown
f2fs: avoid wrong decrypted data from disk
Revert "f2fs: use printk_ratelimited for f2fs_msg"
f2fs: fix unnecessary periodic wakeup of discard thread when dev is busy
f2fs: fix to avoid NULL pointer dereference on se->discard_map
f2fs: add additional sanity check in f2fs_acl_from_disk()
Revert "BACKPORT, FROMLIST: fscrypt: add Speck128/256 support"
Build fix for 076c36fce1
.
Revert "BACKPORT, FROMGIT: crypto: speck - add support for the Speck block cipher"
Revert "FROMGIT: crypto: speck - export common helpers"
Revert "BACKPORT, FROMGIT: crypto: arm/speck - add NEON-accelerated implementation of Speck-XTS"
Revert "BACKPORT, FROMGIT: crypto: speck - add test vectors for Speck128-XTS"
Revert "BACKPORT, FROMGIT: crypto: speck - add test vectors for Speck64-XTS"
Revert "BACKPORT, FROMLIST: crypto: arm64/speck - add NEON-accelerated implementation of Speck-XTS"
Revert "fscrypt: add Speck128/256 support"
UPSTREAM: loop: Add LOOP_SET_BLOCK_SIZE in compat ioctl
BACKPORT: block/loop: set hw_sectors
UPSTREAM: loop: add ioctl for changing logical block size
Conflicts:
fs/ext4/crypto.c
fs/ext4/ext4.h
Change-Id: I8cb2f70b27906879f8e8fdd90e67f438e39701b8
Signed-off-by: Srinivasarao P <spathi@codeaurora.org>
694 lines
16 KiB
C
694 lines
16 KiB
C
/*
|
|
* Copyright (C) 2004, OGAWA Hirofumi
|
|
* Released under GPL v2.
|
|
*/
|
|
|
|
#include <linux/blkdev.h>
|
|
#include "fat.h"
|
|
|
|
struct fatent_operations {
|
|
void (*ent_blocknr)(struct super_block *, int, int *, sector_t *);
|
|
void (*ent_set_ptr)(struct fat_entry *, int);
|
|
int (*ent_bread)(struct super_block *, struct fat_entry *,
|
|
int, sector_t);
|
|
int (*ent_get)(struct fat_entry *);
|
|
void (*ent_put)(struct fat_entry *, int);
|
|
int (*ent_next)(struct fat_entry *);
|
|
};
|
|
|
|
static DEFINE_SPINLOCK(fat12_entry_lock);
|
|
|
|
static void fat12_ent_blocknr(struct super_block *sb, int entry,
|
|
int *offset, sector_t *blocknr)
|
|
{
|
|
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
|
int bytes = entry + (entry >> 1);
|
|
WARN_ON(!fat_valid_entry(sbi, entry));
|
|
*offset = bytes & (sb->s_blocksize - 1);
|
|
*blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits);
|
|
}
|
|
|
|
static void fat_ent_blocknr(struct super_block *sb, int entry,
|
|
int *offset, sector_t *blocknr)
|
|
{
|
|
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
|
int bytes = (entry << sbi->fatent_shift);
|
|
WARN_ON(!fat_valid_entry(sbi, entry));
|
|
*offset = bytes & (sb->s_blocksize - 1);
|
|
*blocknr = sbi->fat_start + (bytes >> sb->s_blocksize_bits);
|
|
}
|
|
|
|
static void fat12_ent_set_ptr(struct fat_entry *fatent, int offset)
|
|
{
|
|
struct buffer_head **bhs = fatent->bhs;
|
|
if (fatent->nr_bhs == 1) {
|
|
WARN_ON(offset >= (bhs[0]->b_size - 1));
|
|
fatent->u.ent12_p[0] = bhs[0]->b_data + offset;
|
|
fatent->u.ent12_p[1] = bhs[0]->b_data + (offset + 1);
|
|
} else {
|
|
WARN_ON(offset != (bhs[0]->b_size - 1));
|
|
fatent->u.ent12_p[0] = bhs[0]->b_data + offset;
|
|
fatent->u.ent12_p[1] = bhs[1]->b_data;
|
|
}
|
|
}
|
|
|
|
static void fat16_ent_set_ptr(struct fat_entry *fatent, int offset)
|
|
{
|
|
WARN_ON(offset & (2 - 1));
|
|
fatent->u.ent16_p = (__le16 *)(fatent->bhs[0]->b_data + offset);
|
|
}
|
|
|
|
static void fat32_ent_set_ptr(struct fat_entry *fatent, int offset)
|
|
{
|
|
WARN_ON(offset & (4 - 1));
|
|
fatent->u.ent32_p = (__le32 *)(fatent->bhs[0]->b_data + offset);
|
|
}
|
|
|
|
static int fat12_ent_bread(struct super_block *sb, struct fat_entry *fatent,
|
|
int offset, sector_t blocknr)
|
|
{
|
|
struct buffer_head **bhs = fatent->bhs;
|
|
|
|
WARN_ON(blocknr < MSDOS_SB(sb)->fat_start);
|
|
fatent->fat_inode = MSDOS_SB(sb)->fat_inode;
|
|
|
|
bhs[0] = sb_bread(sb, blocknr);
|
|
if (!bhs[0])
|
|
goto err;
|
|
|
|
if ((offset + 1) < sb->s_blocksize)
|
|
fatent->nr_bhs = 1;
|
|
else {
|
|
/* This entry is block boundary, it needs the next block */
|
|
blocknr++;
|
|
bhs[1] = sb_bread(sb, blocknr);
|
|
if (!bhs[1])
|
|
goto err_brelse;
|
|
fatent->nr_bhs = 2;
|
|
}
|
|
fat12_ent_set_ptr(fatent, offset);
|
|
return 0;
|
|
|
|
err_brelse:
|
|
brelse(bhs[0]);
|
|
err:
|
|
fat_msg_ratelimit(sb, KERN_ERR,
|
|
"FAT read failed (blocknr %llu)", (llu)blocknr);
|
|
return -EIO;
|
|
}
|
|
|
|
static int fat_ent_bread(struct super_block *sb, struct fat_entry *fatent,
|
|
int offset, sector_t blocknr)
|
|
{
|
|
struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
|
|
|
|
WARN_ON(blocknr < MSDOS_SB(sb)->fat_start);
|
|
fatent->fat_inode = MSDOS_SB(sb)->fat_inode;
|
|
fatent->bhs[0] = sb_bread(sb, blocknr);
|
|
if (!fatent->bhs[0]) {
|
|
fat_msg_ratelimit(sb, KERN_ERR,
|
|
"FAT read failed (blocknr %llu)", (llu)blocknr);
|
|
return -EIO;
|
|
}
|
|
fatent->nr_bhs = 1;
|
|
ops->ent_set_ptr(fatent, offset);
|
|
return 0;
|
|
}
|
|
|
|
static int fat12_ent_get(struct fat_entry *fatent)
|
|
{
|
|
u8 **ent12_p = fatent->u.ent12_p;
|
|
int next;
|
|
|
|
spin_lock(&fat12_entry_lock);
|
|
if (fatent->entry & 1)
|
|
next = (*ent12_p[0] >> 4) | (*ent12_p[1] << 4);
|
|
else
|
|
next = (*ent12_p[1] << 8) | *ent12_p[0];
|
|
spin_unlock(&fat12_entry_lock);
|
|
|
|
next &= 0x0fff;
|
|
if (next >= BAD_FAT12)
|
|
next = FAT_ENT_EOF;
|
|
return next;
|
|
}
|
|
|
|
static int fat16_ent_get(struct fat_entry *fatent)
|
|
{
|
|
int next = le16_to_cpu(*fatent->u.ent16_p);
|
|
WARN_ON((unsigned long)fatent->u.ent16_p & (2 - 1));
|
|
if (next >= BAD_FAT16)
|
|
next = FAT_ENT_EOF;
|
|
return next;
|
|
}
|
|
|
|
static int fat32_ent_get(struct fat_entry *fatent)
|
|
{
|
|
int next = le32_to_cpu(*fatent->u.ent32_p) & 0x0fffffff;
|
|
WARN_ON((unsigned long)fatent->u.ent32_p & (4 - 1));
|
|
if (next >= BAD_FAT32)
|
|
next = FAT_ENT_EOF;
|
|
return next;
|
|
}
|
|
|
|
static void fat12_ent_put(struct fat_entry *fatent, int new)
|
|
{
|
|
u8 **ent12_p = fatent->u.ent12_p;
|
|
|
|
if (new == FAT_ENT_EOF)
|
|
new = EOF_FAT12;
|
|
|
|
spin_lock(&fat12_entry_lock);
|
|
if (fatent->entry & 1) {
|
|
*ent12_p[0] = (new << 4) | (*ent12_p[0] & 0x0f);
|
|
*ent12_p[1] = new >> 4;
|
|
} else {
|
|
*ent12_p[0] = new & 0xff;
|
|
*ent12_p[1] = (*ent12_p[1] & 0xf0) | (new >> 8);
|
|
}
|
|
spin_unlock(&fat12_entry_lock);
|
|
|
|
mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
|
|
if (fatent->nr_bhs == 2)
|
|
mark_buffer_dirty_inode(fatent->bhs[1], fatent->fat_inode);
|
|
}
|
|
|
|
static void fat16_ent_put(struct fat_entry *fatent, int new)
|
|
{
|
|
if (new == FAT_ENT_EOF)
|
|
new = EOF_FAT16;
|
|
|
|
*fatent->u.ent16_p = cpu_to_le16(new);
|
|
mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
|
|
}
|
|
|
|
static void fat32_ent_put(struct fat_entry *fatent, int new)
|
|
{
|
|
WARN_ON(new & 0xf0000000);
|
|
new |= le32_to_cpu(*fatent->u.ent32_p) & ~0x0fffffff;
|
|
*fatent->u.ent32_p = cpu_to_le32(new);
|
|
mark_buffer_dirty_inode(fatent->bhs[0], fatent->fat_inode);
|
|
}
|
|
|
|
static int fat12_ent_next(struct fat_entry *fatent)
|
|
{
|
|
u8 **ent12_p = fatent->u.ent12_p;
|
|
struct buffer_head **bhs = fatent->bhs;
|
|
u8 *nextp = ent12_p[1] + 1 + (fatent->entry & 1);
|
|
|
|
fatent->entry++;
|
|
if (fatent->nr_bhs == 1) {
|
|
WARN_ON(ent12_p[0] > (u8 *)(bhs[0]->b_data +
|
|
(bhs[0]->b_size - 2)));
|
|
WARN_ON(ent12_p[1] > (u8 *)(bhs[0]->b_data +
|
|
(bhs[0]->b_size - 1)));
|
|
if (nextp < (u8 *)(bhs[0]->b_data + (bhs[0]->b_size - 1))) {
|
|
ent12_p[0] = nextp - 1;
|
|
ent12_p[1] = nextp;
|
|
return 1;
|
|
}
|
|
} else {
|
|
WARN_ON(ent12_p[0] != (u8 *)(bhs[0]->b_data +
|
|
(bhs[0]->b_size - 1)));
|
|
WARN_ON(ent12_p[1] != (u8 *)bhs[1]->b_data);
|
|
ent12_p[0] = nextp - 1;
|
|
ent12_p[1] = nextp;
|
|
brelse(bhs[0]);
|
|
bhs[0] = bhs[1];
|
|
fatent->nr_bhs = 1;
|
|
return 1;
|
|
}
|
|
ent12_p[0] = NULL;
|
|
ent12_p[1] = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static int fat16_ent_next(struct fat_entry *fatent)
|
|
{
|
|
const struct buffer_head *bh = fatent->bhs[0];
|
|
fatent->entry++;
|
|
if (fatent->u.ent16_p < (__le16 *)(bh->b_data + (bh->b_size - 2))) {
|
|
fatent->u.ent16_p++;
|
|
return 1;
|
|
}
|
|
fatent->u.ent16_p = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static int fat32_ent_next(struct fat_entry *fatent)
|
|
{
|
|
const struct buffer_head *bh = fatent->bhs[0];
|
|
fatent->entry++;
|
|
if (fatent->u.ent32_p < (__le32 *)(bh->b_data + (bh->b_size - 4))) {
|
|
fatent->u.ent32_p++;
|
|
return 1;
|
|
}
|
|
fatent->u.ent32_p = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static struct fatent_operations fat12_ops = {
|
|
.ent_blocknr = fat12_ent_blocknr,
|
|
.ent_set_ptr = fat12_ent_set_ptr,
|
|
.ent_bread = fat12_ent_bread,
|
|
.ent_get = fat12_ent_get,
|
|
.ent_put = fat12_ent_put,
|
|
.ent_next = fat12_ent_next,
|
|
};
|
|
|
|
static struct fatent_operations fat16_ops = {
|
|
.ent_blocknr = fat_ent_blocknr,
|
|
.ent_set_ptr = fat16_ent_set_ptr,
|
|
.ent_bread = fat_ent_bread,
|
|
.ent_get = fat16_ent_get,
|
|
.ent_put = fat16_ent_put,
|
|
.ent_next = fat16_ent_next,
|
|
};
|
|
|
|
static struct fatent_operations fat32_ops = {
|
|
.ent_blocknr = fat_ent_blocknr,
|
|
.ent_set_ptr = fat32_ent_set_ptr,
|
|
.ent_bread = fat_ent_bread,
|
|
.ent_get = fat32_ent_get,
|
|
.ent_put = fat32_ent_put,
|
|
.ent_next = fat32_ent_next,
|
|
};
|
|
|
|
static inline void lock_fat(struct msdos_sb_info *sbi)
|
|
{
|
|
mutex_lock(&sbi->fat_lock);
|
|
}
|
|
|
|
static inline void unlock_fat(struct msdos_sb_info *sbi)
|
|
{
|
|
mutex_unlock(&sbi->fat_lock);
|
|
}
|
|
|
|
void fat_ent_access_init(struct super_block *sb)
|
|
{
|
|
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
|
|
|
mutex_init(&sbi->fat_lock);
|
|
|
|
switch (sbi->fat_bits) {
|
|
case 32:
|
|
sbi->fatent_shift = 2;
|
|
sbi->fatent_ops = &fat32_ops;
|
|
break;
|
|
case 16:
|
|
sbi->fatent_shift = 1;
|
|
sbi->fatent_ops = &fat16_ops;
|
|
break;
|
|
case 12:
|
|
sbi->fatent_shift = -1;
|
|
sbi->fatent_ops = &fat12_ops;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void mark_fsinfo_dirty(struct super_block *sb)
|
|
{
|
|
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
|
|
|
if (sb->s_flags & MS_RDONLY || sbi->fat_bits != 32)
|
|
return;
|
|
|
|
__mark_inode_dirty(sbi->fsinfo_inode, I_DIRTY_SYNC);
|
|
}
|
|
|
|
static inline int fat_ent_update_ptr(struct super_block *sb,
|
|
struct fat_entry *fatent,
|
|
int offset, sector_t blocknr)
|
|
{
|
|
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
|
struct fatent_operations *ops = sbi->fatent_ops;
|
|
struct buffer_head **bhs = fatent->bhs;
|
|
|
|
/* Is this fatent's blocks including this entry? */
|
|
if (!fatent->nr_bhs || bhs[0]->b_blocknr != blocknr)
|
|
return 0;
|
|
if (sbi->fat_bits == 12) {
|
|
if ((offset + 1) < sb->s_blocksize) {
|
|
/* This entry is on bhs[0]. */
|
|
if (fatent->nr_bhs == 2) {
|
|
brelse(bhs[1]);
|
|
fatent->nr_bhs = 1;
|
|
}
|
|
} else {
|
|
/* This entry needs the next block. */
|
|
if (fatent->nr_bhs != 2)
|
|
return 0;
|
|
if (bhs[1]->b_blocknr != (blocknr + 1))
|
|
return 0;
|
|
}
|
|
}
|
|
ops->ent_set_ptr(fatent, offset);
|
|
return 1;
|
|
}
|
|
|
|
int fat_ent_read(struct inode *inode, struct fat_entry *fatent, int entry)
|
|
{
|
|
struct super_block *sb = inode->i_sb;
|
|
struct msdos_sb_info *sbi = MSDOS_SB(inode->i_sb);
|
|
struct fatent_operations *ops = sbi->fatent_ops;
|
|
int err, offset;
|
|
sector_t blocknr;
|
|
|
|
if (!fat_valid_entry(sbi, entry)) {
|
|
fatent_brelse(fatent);
|
|
fat_fs_error(sb, "invalid access to FAT (entry 0x%08x)", entry);
|
|
return -EIO;
|
|
}
|
|
|
|
fatent_set_entry(fatent, entry);
|
|
ops->ent_blocknr(sb, entry, &offset, &blocknr);
|
|
|
|
if (!fat_ent_update_ptr(sb, fatent, offset, blocknr)) {
|
|
fatent_brelse(fatent);
|
|
err = ops->ent_bread(sb, fatent, offset, blocknr);
|
|
if (err)
|
|
return err;
|
|
}
|
|
return ops->ent_get(fatent);
|
|
}
|
|
|
|
/* FIXME: We can write the blocks as more big chunk. */
|
|
static int fat_mirror_bhs(struct super_block *sb, struct buffer_head **bhs,
|
|
int nr_bhs)
|
|
{
|
|
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
|
struct buffer_head *c_bh;
|
|
int err, n, copy;
|
|
|
|
err = 0;
|
|
for (copy = 1; copy < sbi->fats; copy++) {
|
|
sector_t backup_fat = sbi->fat_length * copy;
|
|
|
|
for (n = 0; n < nr_bhs; n++) {
|
|
c_bh = sb_getblk(sb, backup_fat + bhs[n]->b_blocknr);
|
|
if (!c_bh) {
|
|
err = -ENOMEM;
|
|
goto error;
|
|
}
|
|
memcpy(c_bh->b_data, bhs[n]->b_data, sb->s_blocksize);
|
|
set_buffer_uptodate(c_bh);
|
|
mark_buffer_dirty_inode(c_bh, sbi->fat_inode);
|
|
if (sb->s_flags & MS_SYNCHRONOUS)
|
|
err = sync_dirty_buffer(c_bh);
|
|
brelse(c_bh);
|
|
if (err)
|
|
goto error;
|
|
}
|
|
}
|
|
error:
|
|
return err;
|
|
}
|
|
|
|
int fat_ent_write(struct inode *inode, struct fat_entry *fatent,
|
|
int new, int wait)
|
|
{
|
|
struct super_block *sb = inode->i_sb;
|
|
struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
|
|
int err;
|
|
|
|
ops->ent_put(fatent, new);
|
|
if (wait) {
|
|
err = fat_sync_bhs(fatent->bhs, fatent->nr_bhs);
|
|
if (err)
|
|
return err;
|
|
}
|
|
return fat_mirror_bhs(sb, fatent->bhs, fatent->nr_bhs);
|
|
}
|
|
|
|
static inline int fat_ent_next(struct msdos_sb_info *sbi,
|
|
struct fat_entry *fatent)
|
|
{
|
|
if (sbi->fatent_ops->ent_next(fatent)) {
|
|
if (fatent->entry < sbi->max_cluster)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline int fat_ent_read_block(struct super_block *sb,
|
|
struct fat_entry *fatent)
|
|
{
|
|
struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
|
|
sector_t blocknr;
|
|
int offset;
|
|
|
|
fatent_brelse(fatent);
|
|
ops->ent_blocknr(sb, fatent->entry, &offset, &blocknr);
|
|
return ops->ent_bread(sb, fatent, offset, blocknr);
|
|
}
|
|
|
|
static void fat_collect_bhs(struct buffer_head **bhs, int *nr_bhs,
|
|
struct fat_entry *fatent)
|
|
{
|
|
int n, i;
|
|
|
|
for (n = 0; n < fatent->nr_bhs; n++) {
|
|
for (i = 0; i < *nr_bhs; i++) {
|
|
if (fatent->bhs[n] == bhs[i])
|
|
break;
|
|
}
|
|
if (i == *nr_bhs) {
|
|
get_bh(fatent->bhs[n]);
|
|
bhs[i] = fatent->bhs[n];
|
|
(*nr_bhs)++;
|
|
}
|
|
}
|
|
}
|
|
|
|
int fat_alloc_clusters(struct inode *inode, int *cluster, int nr_cluster)
|
|
{
|
|
struct super_block *sb = inode->i_sb;
|
|
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
|
struct fatent_operations *ops = sbi->fatent_ops;
|
|
struct fat_entry fatent, prev_ent;
|
|
struct buffer_head *bhs[MAX_BUF_PER_PAGE];
|
|
int i, count, err, nr_bhs, idx_clus;
|
|
|
|
BUG_ON(nr_cluster > (MAX_BUF_PER_PAGE / 2)); /* fixed limit */
|
|
|
|
lock_fat(sbi);
|
|
if (sbi->free_clusters != -1 && sbi->free_clus_valid &&
|
|
sbi->free_clusters < nr_cluster) {
|
|
unlock_fat(sbi);
|
|
return -ENOSPC;
|
|
}
|
|
|
|
err = nr_bhs = idx_clus = 0;
|
|
count = FAT_START_ENT;
|
|
fatent_init(&prev_ent);
|
|
fatent_init(&fatent);
|
|
fatent_set_entry(&fatent, sbi->prev_free + 1);
|
|
while (count < sbi->max_cluster) {
|
|
if (fatent.entry >= sbi->max_cluster)
|
|
fatent.entry = FAT_START_ENT;
|
|
fatent_set_entry(&fatent, fatent.entry);
|
|
err = fat_ent_read_block(sb, &fatent);
|
|
if (err)
|
|
goto out;
|
|
|
|
/* Find the free entries in a block */
|
|
do {
|
|
if (ops->ent_get(&fatent) == FAT_ENT_FREE) {
|
|
int entry = fatent.entry;
|
|
|
|
/* make the cluster chain */
|
|
ops->ent_put(&fatent, FAT_ENT_EOF);
|
|
if (prev_ent.nr_bhs)
|
|
ops->ent_put(&prev_ent, entry);
|
|
|
|
fat_collect_bhs(bhs, &nr_bhs, &fatent);
|
|
|
|
sbi->prev_free = entry;
|
|
if (sbi->free_clusters != -1)
|
|
sbi->free_clusters--;
|
|
|
|
cluster[idx_clus] = entry;
|
|
idx_clus++;
|
|
if (idx_clus == nr_cluster)
|
|
goto out;
|
|
|
|
/*
|
|
* fat_collect_bhs() gets ref-count of bhs,
|
|
* so we can still use the prev_ent.
|
|
*/
|
|
prev_ent = fatent;
|
|
}
|
|
count++;
|
|
if (count == sbi->max_cluster)
|
|
break;
|
|
} while (fat_ent_next(sbi, &fatent));
|
|
}
|
|
|
|
/* Couldn't allocate the free entries */
|
|
sbi->free_clusters = 0;
|
|
sbi->free_clus_valid = 1;
|
|
err = -ENOSPC;
|
|
|
|
out:
|
|
unlock_fat(sbi);
|
|
mark_fsinfo_dirty(sb);
|
|
fatent_brelse(&fatent);
|
|
if (!err) {
|
|
if (inode_needs_sync(inode))
|
|
err = fat_sync_bhs(bhs, nr_bhs);
|
|
if (!err)
|
|
err = fat_mirror_bhs(sb, bhs, nr_bhs);
|
|
}
|
|
for (i = 0; i < nr_bhs; i++)
|
|
brelse(bhs[i]);
|
|
|
|
if (err && idx_clus)
|
|
fat_free_clusters(inode, cluster[0]);
|
|
|
|
return err;
|
|
}
|
|
|
|
int fat_free_clusters(struct inode *inode, int cluster)
|
|
{
|
|
struct super_block *sb = inode->i_sb;
|
|
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
|
struct fatent_operations *ops = sbi->fatent_ops;
|
|
struct fat_entry fatent;
|
|
struct buffer_head *bhs[MAX_BUF_PER_PAGE];
|
|
int i, err, nr_bhs;
|
|
int first_cl = cluster, dirty_fsinfo = 0;
|
|
|
|
nr_bhs = 0;
|
|
fatent_init(&fatent);
|
|
lock_fat(sbi);
|
|
do {
|
|
cluster = fat_ent_read(inode, &fatent, cluster);
|
|
if (cluster < 0) {
|
|
err = cluster;
|
|
goto error;
|
|
} else if (cluster == FAT_ENT_FREE) {
|
|
fat_fs_error(sb, "%s: deleting FAT entry beyond EOF",
|
|
__func__);
|
|
err = -EIO;
|
|
goto error;
|
|
}
|
|
|
|
if (sbi->options.discard) {
|
|
/*
|
|
* Issue discard for the sectors we no longer
|
|
* care about, batching contiguous clusters
|
|
* into one request
|
|
*/
|
|
if (cluster != fatent.entry + 1) {
|
|
int nr_clus = fatent.entry - first_cl + 1;
|
|
|
|
sb_issue_discard(sb,
|
|
fat_clus_to_blknr(sbi, first_cl),
|
|
nr_clus * sbi->sec_per_clus,
|
|
GFP_NOFS, 0);
|
|
|
|
first_cl = cluster;
|
|
}
|
|
}
|
|
|
|
ops->ent_put(&fatent, FAT_ENT_FREE);
|
|
if (sbi->free_clusters != -1) {
|
|
sbi->free_clusters++;
|
|
dirty_fsinfo = 1;
|
|
}
|
|
|
|
if (nr_bhs + fatent.nr_bhs > MAX_BUF_PER_PAGE) {
|
|
if (sb->s_flags & MS_SYNCHRONOUS) {
|
|
err = fat_sync_bhs(bhs, nr_bhs);
|
|
if (err)
|
|
goto error;
|
|
}
|
|
err = fat_mirror_bhs(sb, bhs, nr_bhs);
|
|
if (err)
|
|
goto error;
|
|
for (i = 0; i < nr_bhs; i++)
|
|
brelse(bhs[i]);
|
|
nr_bhs = 0;
|
|
}
|
|
fat_collect_bhs(bhs, &nr_bhs, &fatent);
|
|
} while (cluster != FAT_ENT_EOF);
|
|
|
|
if (sb->s_flags & MS_SYNCHRONOUS) {
|
|
err = fat_sync_bhs(bhs, nr_bhs);
|
|
if (err)
|
|
goto error;
|
|
}
|
|
err = fat_mirror_bhs(sb, bhs, nr_bhs);
|
|
error:
|
|
fatent_brelse(&fatent);
|
|
for (i = 0; i < nr_bhs; i++)
|
|
brelse(bhs[i]);
|
|
unlock_fat(sbi);
|
|
if (dirty_fsinfo)
|
|
mark_fsinfo_dirty(sb);
|
|
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(fat_free_clusters);
|
|
|
|
/* 128kb is the whole sectors for FAT12 and FAT16 */
|
|
#define FAT_READA_SIZE (128 * 1024)
|
|
|
|
static void fat_ent_reada(struct super_block *sb, struct fat_entry *fatent,
|
|
unsigned long reada_blocks)
|
|
{
|
|
struct fatent_operations *ops = MSDOS_SB(sb)->fatent_ops;
|
|
sector_t blocknr;
|
|
int i, offset;
|
|
|
|
ops->ent_blocknr(sb, fatent->entry, &offset, &blocknr);
|
|
|
|
for (i = 0; i < reada_blocks; i++)
|
|
sb_breadahead(sb, blocknr + i);
|
|
}
|
|
|
|
int fat_count_free_clusters(struct super_block *sb)
|
|
{
|
|
struct msdos_sb_info *sbi = MSDOS_SB(sb);
|
|
struct fatent_operations *ops = sbi->fatent_ops;
|
|
struct fat_entry fatent;
|
|
unsigned long reada_blocks, reada_mask, cur_block;
|
|
int err = 0, free;
|
|
|
|
lock_fat(sbi);
|
|
if (sbi->free_clusters != -1 && sbi->free_clus_valid)
|
|
goto out;
|
|
|
|
reada_blocks = FAT_READA_SIZE >> sb->s_blocksize_bits;
|
|
reada_mask = reada_blocks - 1;
|
|
cur_block = 0;
|
|
|
|
free = 0;
|
|
fatent_init(&fatent);
|
|
fatent_set_entry(&fatent, FAT_START_ENT);
|
|
while (fatent.entry < sbi->max_cluster) {
|
|
/* readahead of fat blocks */
|
|
if ((cur_block & reada_mask) == 0) {
|
|
unsigned long rest = sbi->fat_length - cur_block;
|
|
fat_ent_reada(sb, &fatent, min(reada_blocks, rest));
|
|
}
|
|
cur_block++;
|
|
|
|
err = fat_ent_read_block(sb, &fatent);
|
|
if (err)
|
|
goto out;
|
|
|
|
do {
|
|
if (ops->ent_get(&fatent) == FAT_ENT_FREE)
|
|
free++;
|
|
} while (fat_ent_next(sbi, &fatent));
|
|
cond_resched();
|
|
}
|
|
sbi->free_clusters = free;
|
|
sbi->free_clus_valid = 1;
|
|
mark_fsinfo_dirty(sb);
|
|
fatent_brelse(&fatent);
|
|
out:
|
|
unlock_fat(sbi);
|
|
return err;
|
|
}
|