📄 fs_disk.c
字号:
/* * GRUB -- GRand Unified Bootloader * Copyright (C) 2002,2003,2004,2006 Free Software Foundation, Inc. * Copyright 2006 Pawel Kolodziejski - adopted to linux native bootloader * * GRUB is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GRUB; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include "fs_types.h"/* The maximum number of disk caches. */#define GRUB_DISK_CACHE_NUM 1021/* The size of a disk cache in sector units. */#define GRUB_DISK_CACHE_SIZE 8#define GRUB_DISK_CACHE_BITS 3#define CACHE_ADDRESS 0xA3100400/* Disk cache. */struct grub_disk_cache { grub_disk_addr_t sector; char *data; int lock;};static unsigned grub_disk_cache_get_index(grub_disk_addr_t sector) { return ((unsigned)(sector >> GRUB_DISK_CACHE_BITS)) % GRUB_DISK_CACHE_NUM;}static void grub_disk_cache_invalidate(grub_disk_addr_t sector) { unsigned index; struct grub_disk_cache *cache; struct grub_disk_cache *grub_disk_cache_table = (struct grub_disk_cache *)CACHE_ADDRESS; sector &= ~(GRUB_DISK_CACHE_SIZE - 1); index = grub_disk_cache_get_index(sector); cache = grub_disk_cache_table + index; if (cache->sector == sector && cache->data) { cache->lock = 1; free(cache->data); cache->data = 0; cache->lock = 0; }}void grub_disk_cache_invalidate_all(void) { unsigned i; struct grub_disk_cache *grub_disk_cache_table = (struct grub_disk_cache *)CACHE_ADDRESS; for (i = 0; i < GRUB_DISK_CACHE_NUM; i++) { struct grub_disk_cache *cache = grub_disk_cache_table + i; if (cache->data && !cache->lock) { free(cache->data); cache->data = 0; } }}static char *grub_disk_cache_fetch(grub_disk_addr_t sector) { struct grub_disk_cache *cache; unsigned index; struct grub_disk_cache *grub_disk_cache_table = (struct grub_disk_cache *)CACHE_ADDRESS; index = grub_disk_cache_get_index(sector); cache = grub_disk_cache_table + index; if (cache->sector == sector) { cache->lock = 1; return cache->data; } return 0;}static void grub_disk_cache_unlock(grub_disk_addr_t sector) { struct grub_disk_cache *cache; unsigned index; struct grub_disk_cache *grub_disk_cache_table = (struct grub_disk_cache *)CACHE_ADDRESS; index = grub_disk_cache_get_index(sector); cache = grub_disk_cache_table + index; if (cache->sector == sector) cache->lock = 0;}static grub_err_t grub_disk_cache_store(grub_disk_addr_t sector, const char *data) { unsigned index; struct grub_disk_cache *cache; struct grub_disk_cache *grub_disk_cache_table = (struct grub_disk_cache *)CACHE_ADDRESS; grub_disk_cache_invalidate(sector); index = grub_disk_cache_get_index(sector); cache = grub_disk_cache_table + index; cache->data = (char *)malloc(GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS); if (!cache->data) return grub_errno; memcpy(cache->data, data, GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS); cache->sector = sector; return GRUB_ERR_NONE;}static grub_err_t grub_disk_check_range(grub_disk_t disk, grub_disk_addr_t *sector, grub_off_t *offset, grub_size_t size) { *sector += *offset >> GRUB_DISK_SECTOR_BITS; *offset &= GRUB_DISK_SECTOR_SIZE - 1; if (disk->pcount) { grub_disk_addr_t start; grub_uint64_t len; start = disk->ptable[disk->cur_ptable].sector_start; len = disk->ptable[disk->cur_ptable].sector_length; if (*sector >= len || len - *sector < ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS)) return grub_error(GRUB_ERR_OUT_OF_RANGE, "out of partition"); *sector += start; } if (disk->total_sectors <= *sector || ((*offset + size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS) > disk->total_sectors - *sector) return grub_error(GRUB_ERR_OUT_OF_RANGE, "out of disk"); return GRUB_ERR_NONE;}static grub_err_t grub_raw_disk_read(grub_disk_t disk, grub_off_t sector, grub_off_t size, char *buf) { while (size) { if (sector >= disk->total_sectors) return GRUB_ERR_OUT_OF_RANGE; disk->read(buf, (int)sector); buf += GRUB_DISK_SECTOR_SIZE; sector++; size--; } return GRUB_ERR_NONE;}/* Read data from the disk. */grub_err_t grub_disk_read(grub_disk_t disk, grub_disk_addr_t sector, grub_off_t offset, grub_size_t size, char *buf) { char *tmp_buf; unsigned real_offset; /* First of all, check if the region is within the disk. */ if (grub_disk_check_range(disk, §or, &offset, size) != GRUB_ERR_NONE) return grub_errno; real_offset = (unsigned)offset; /* Allocate a temporary buffer. */ tmp_buf = (char *)malloc(GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS); if (!tmp_buf) return grub_errno; /* Until SIZE is zero... */ while (size) { char *data; grub_disk_addr_t start_sector; grub_size_t len; grub_size_t pos; /* For reading bulk data. */ start_sector = sector & ~(GRUB_DISK_CACHE_SIZE - 1); pos = (grub_size_t)(sector - start_sector) << GRUB_DISK_SECTOR_BITS; len = ((GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS) - pos - real_offset); if (len > size) len = size; /* Fetch the cache. */ data = grub_disk_cache_fetch(start_sector); if (data) { /* Just copy it! */ memcpy(buf, data + pos + real_offset, len); grub_disk_cache_unlock(start_sector); } else { /* Otherwise read data from the disk actually. */ if (grub_raw_disk_read(disk, start_sector, GRUB_DISK_CACHE_SIZE, tmp_buf) != GRUB_ERR_NONE) { /* Uggh... Failed. Instead, just read necessary data. */ unsigned num; grub_set_error(GRUB_ERR_NONE); /* If more data is required, no way. */ if (pos + size >= (GRUB_DISK_SECTOR_SIZE << GRUB_DISK_CACHE_BITS)) goto finish; num = ((size + GRUB_DISK_SECTOR_SIZE - 1) >> GRUB_DISK_SECTOR_BITS); if (grub_raw_disk_read(disk, sector, num, tmp_buf)) goto finish; memcpy(buf, tmp_buf + real_offset, size); /* Call the read hook, if any. */ if (disk->read_hook) while (size) { (disk->read_hook)(sector, real_offset, ((size > GRUB_DISK_SECTOR_SIZE) ? GRUB_DISK_SECTOR_SIZE : size)); sector++; size -= GRUB_DISK_SECTOR_SIZE - real_offset; real_offset = 0; } /* This must be the end. */ goto finish; } /* Copy it and store it in the disk cache. */ memcpy(buf, tmp_buf + pos + real_offset, len); grub_disk_cache_store(start_sector, tmp_buf); } /* Call the read hook, if any. */ if (disk->read_hook) { grub_disk_addr_t s = sector; grub_size_t l = len; while (l) { (disk->read_hook)(s, real_offset, ((l > GRUB_DISK_SECTOR_SIZE) ? GRUB_DISK_SECTOR_SIZE : l)); if (l < GRUB_DISK_SECTOR_SIZE - real_offset) break; s++; l -= GRUB_DISK_SECTOR_SIZE - real_offset; real_offset = 0; } } sector = start_sector + GRUB_DISK_CACHE_SIZE; buf += len; size -= len; real_offset = 0; }finish: free(tmp_buf); return grub_errno;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -