- struct msdos_sb_info *sbi = MSDOS_SB(sb);
- struct buffer_head *bh, *bh2, *c_bh, *c_bh2;
- unsigned char *p_first, *p_last;
- int copy, first, last, next, b;
-
- if (sbi->fat_bits == 32) {
- first = last = nr*4;
- } else if (sbi->fat_bits == 16) {
- first = last = nr*2;
- } else {
- first = nr*3/2;
- last = first+1;
- }
- b = sbi->fat_start + (first >> sb->s_blocksize_bits);
- if (!(bh = sb_bread(sb, b))) {
- printk(KERN_ERR "FAT: bread(block %d) in"
- " fat_access failed\n", b);
- return -EIO;
- }
- if ((first >> sb->s_blocksize_bits) == (last >> sb->s_blocksize_bits)) {
- bh2 = bh;
- } else {
- if (!(bh2 = sb_bread(sb, b + 1))) {
- brelse(bh);
- printk(KERN_ERR "FAT: bread(block %d) in"
- " fat_access failed\n", b + 1);
- return -EIO;
- }
- }
- if (sbi->fat_bits == 32) {
- p_first = p_last = NULL; /* GCC needs that stuff */
- next = CF_LE_L(((__u32 *) bh->b_data)[(first &
- (sb->s_blocksize - 1)) >> 2]);
- /* Fscking Microsoft marketing department. Their "32" is 28. */
- next &= 0x0fffffff;
- } else if (sbi->fat_bits == 16) {
- p_first = p_last = NULL; /* GCC needs that stuff */
- next = CF_LE_W(((__u16 *) bh->b_data)[(first &
- (sb->s_blocksize - 1)) >> 1]);
- } else {
- p_first = &((__u8 *)bh->b_data)[first & (sb->s_blocksize - 1)];
- p_last = &((__u8 *)bh2->b_data)[(first + 1) & (sb->s_blocksize - 1)];
- if (nr & 1)
- next = ((*p_first >> 4) | (*p_last << 4)) & 0xfff;
- else
- next = (*p_first+(*p_last << 8)) & 0xfff;
- }
- if (new_value != -1) {
- if (sbi->fat_bits == 32) {
- ((__u32 *)bh->b_data)[(first & (sb->s_blocksize - 1)) >> 2]
- = CT_LE_L(new_value);
- } else if (sbi->fat_bits == 16) {
- ((__u16 *)bh->b_data)[(first & (sb->s_blocksize - 1)) >> 1]
- = CT_LE_W(new_value);
- } else {
- if (nr & 1) {
- *p_first = (*p_first & 0xf) | (new_value << 4);
- *p_last = new_value >> 4;
- }
- else {
- *p_first = new_value & 0xff;
- *p_last = (*p_last & 0xf0) | (new_value >> 8);
- }
- mark_buffer_dirty(bh2);
- }
- mark_buffer_dirty(bh);
- for (copy = 1; copy < sbi->fats; copy++) {
- b = sbi->fat_start + (first >> sb->s_blocksize_bits)
- + sbi->fat_length * copy;
- if (!(c_bh = sb_bread(sb, b)))
- break;
- if (bh != bh2) {
- if (!(c_bh2 = sb_bread(sb, b+1))) {
- brelse(c_bh);
- break;
- }
- memcpy(c_bh2->b_data, bh2->b_data, sb->s_blocksize);
- mark_buffer_dirty(c_bh2);
- brelse(c_bh2);
- }
- memcpy(c_bh->b_data, bh->b_data, sb->s_blocksize);
- mark_buffer_dirty(c_bh);
- brelse(c_bh);
- }
- }
- brelse(bh);
- if (bh != bh2)
- brelse(bh2);
- return next;