android_kernel_oneplus_msm8998/fs/fat/fatent.c
Srinivasarao P b28608bd14 Merge android-4.4.163 (0ca3fca) into msm-4.4
* 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>
2018-11-15 17:07:08 +05:30

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;
}