/* Read LEN bytes from the file described by DATA starting with byte POS. Return the amount of read bytes in READ. */ static grub_ssize_t grub_hfs_read_file (struct grub_hfs_data *data, void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector, unsigned offset, unsigned length), grub_off_t pos, grub_size_t len, char *buf) { grub_off_t i; grub_off_t blockcnt; blockcnt = grub_divmod64 (((len + pos) + data->blksz - 1), data->blksz, 0); for (i = grub_divmod64 (pos, data->blksz, 0); i < blockcnt; i++) { grub_disk_addr_t blknr; grub_off_t blockoff; grub_off_t blockend = data->blksz; int skipfirst = 0; grub_divmod64 (pos, data->blksz, &blockoff); blknr = grub_hfs_block (data, data->extents, data->fileid, i, 1); if (grub_errno) return -1; /* Last block. */ if (i == blockcnt - 1) { grub_divmod64 ((len + pos), data->blksz, &blockend); /* The last portion is exactly EXT2_BLOCK_SIZE (data). */ if (! blockend) blockend = data->blksz; } /* First block. */ if (i == grub_divmod64 (pos, data->blksz, 0)) { skipfirst = blockoff; blockend -= skipfirst; } /* If the block number is 0 this block is not stored on disk but is zero filled instead. */ if (blknr) { data->disk->read_hook = read_hook; grub_disk_read (data->disk, blknr, skipfirst, blockend, buf); data->disk->read_hook = 0; if (grub_errno) return -1; } buf += data->blksz - skipfirst; } return len; }
static grub_disk_addr_t grub_affs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock) { int links; grub_uint32_t pos; int block = node->block; struct grub_affs_file file; struct grub_affs_data *data = node->data; grub_uint32_t mod; /* Find the block that points to the fileblock we are looking up by following the chain until the right table is reached. */ for (links = grub_divmod64 (fileblock, data->htsize, &mod); links; links--) { grub_disk_read (data->disk, block + data->blocksize - 1, data->blocksize * (GRUB_DISK_SECTOR_SIZE - GRUB_AFFS_FILE_LOCATION), sizeof (file), &file); if (grub_errno) return 0; block = grub_be_to_cpu32 (file.extension); } /* Translate the fileblock to the block within the right table. */ fileblock = mod; grub_disk_read (data->disk, block, GRUB_AFFS_BLOCKPTR_OFFSET + (data->htsize - fileblock - 1) * sizeof (pos), sizeof (pos), &pos); if (grub_errno) return 0; return grub_be_to_cpu32 (pos); }
static grub_uint64_t rpi_timer_ms (void) { static grub_uint32_t last = 0, high = 0; grub_uint32_t cur = *(volatile grub_uint32_t *) 0x20003004; if (cur < last) high++; last = cur; return grub_divmod64 ((((grub_uint64_t) high) << 32) | cur, 1000, 0); }
/* Calculate the time in milliseconds since the epoch based on the RTC. */ grub_uint64_t grub_rtc_get_time_ms (void) { /* By dimensional analysis: 1000 ms N rtc ticks 1 s ------- * ----------- * ----------- = 1000*N/T ms 1 s 1 T rtc ticks */ grub_uint64_t ticks_ms_per_sec = ((grub_uint64_t) 1000) * grub_get_rtc (); return grub_divmod64 (ticks_ms_per_sec, GRUB_TICKS_PER_SECOND, 0); }
static grub_err_t grub_biosdisk_open (const char *name, grub_disk_t disk) { grub_uint64_t total_sectors = 0; int drive; struct grub_biosdisk_data *data; drive = grub_biosdisk_get_drive (name); if (drive < 0) return grub_errno; disk->has_partitions = ((drive & 0x80) && (drive != cd_drive)); disk->id = drive; data = (struct grub_biosdisk_data *) grub_zalloc (sizeof (*data)); if (! data) return grub_errno; data->drive = drive; if ((cd_drive) && (drive == cd_drive)) { data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM; data->sectors = 32; total_sectors = GRUB_ULONG_MAX; /* TODO: get the correct size. */ } else if (drive & 0x80) { /* HDD */ int version; version = grub_biosdisk_check_int13_extensions (drive); if (version) { struct grub_biosdisk_drp *drp = (struct grub_biosdisk_drp *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR; /* Clear out the DRP. */ grub_memset (drp, 0, sizeof (*drp)); drp->size = sizeof (*drp); if (! grub_biosdisk_get_diskinfo_int13_extensions (drive, drp)) { data->flags = GRUB_BIOSDISK_FLAG_LBA; if (drp->total_sectors) total_sectors = drp->total_sectors; else /* Some buggy BIOSes doesn't return the total sectors correctly but returns zero. So if it is zero, compute it by C/H/S returned by the LBA BIOS call. */ total_sectors = drp->cylinders * drp->heads * drp->sectors; } } } if (! (data->flags & GRUB_BIOSDISK_FLAG_CDROM)) { if (grub_biosdisk_get_diskinfo_standard (drive, &data->cylinders, &data->heads, &data->sectors) != 0) { if (total_sectors && (data->flags & GRUB_BIOSDISK_FLAG_LBA)) { data->sectors = 63; data->heads = 255; data->cylinders = grub_divmod64 (total_sectors + data->heads * data->sectors - 1, data->heads * data->sectors, 0); } else { grub_free (data); return grub_error (GRUB_ERR_BAD_DEVICE, "%s cannot get C/H/S values", disk->name); } } if (! total_sectors) total_sectors = data->cylinders * data->heads * data->sectors; } disk->total_sectors = total_sectors; disk->data = data; return GRUB_ERR_NONE; }
/* Helper for grub_ls_list_files. */ static int print_files_long (const char *filename, const struct grub_dirhook_info *info, void *data) { struct grub_ls_list_files_ctx *ctx = data; if ((! ctx->all) && (filename[0] == '.')) return 0; if (! info->dir) { grub_file_t file; char *pathname; if (ctx->dirname[grub_strlen (ctx->dirname) - 1] == '/') pathname = grub_xasprintf ("%s%s", ctx->dirname, filename); else pathname = grub_xasprintf ("%s/%s", ctx->dirname, filename); if (!pathname) return 1; /* XXX: For ext2fs symlinks are detected as files while they should be reported as directories. */ grub_file_filter_disable_compression (); file = grub_file_open (pathname); if (! file) { grub_errno = 0; grub_free (pathname); return 0; } if (! ctx->human) grub_printf ("%-12llu", (unsigned long long) file->size); else { grub_uint64_t fsize = file->size * 100ULL; grub_uint64_t fsz = file->size; int units = 0; char buf[20]; while (fsz / 1024) { fsize = (fsize + 512) / 1024; fsz /= 1024; units++; } if (units) { grub_uint64_t whole, fraction; whole = grub_divmod64 (fsize, 100, &fraction); grub_snprintf (buf, sizeof (buf), "%" PRIuGRUB_UINT64_T ".%02" PRIuGRUB_UINT64_T "%c", whole, fraction, grub_human_sizes[units]); grub_printf ("%-12s", buf); } else grub_printf ("%-12llu", (unsigned long long) file->size); } grub_file_close (file); grub_free (pathname); } else grub_printf ("%-12s", _("DIR")); if (info->mtimeset) { struct grub_datetime datetime; grub_unixtime2datetime (info->mtime, &datetime); if (ctx->human) grub_printf (" %d-%02d-%02d %02d:%02d:%02d %-11s ", datetime.year, datetime.month, datetime.day, datetime.hour, datetime.minute, datetime.second, grub_get_weekday_name (&datetime)); else grub_printf (" %04d%02d%02d%02d%02d%02d ", datetime.year, datetime.month, datetime.day, datetime.hour, datetime.minute, datetime.second); } grub_printf ("%s%s\n", filename, info->dir ? "/" : ""); return 0; }
unsigned long long grub_strtoull (const char *str, char **end, int base) { unsigned long long num = 0; int found = 0; /* Skip white spaces. */ while (*str && grub_isspace (*str)) str++; /* Guess the base, if not specified. The prefix `0x' means 16, and the prefix `0' means 8. */ if (str[0] == '0') { if (str[1] == 'x') { if (base == 0 || base == 16) { base = 16; str += 2; } } else if (base == 0 && str[1] >= '0' && str[1] <= '7') base = 8; } if (base == 0) base = 10; while (*str) { unsigned long digit; digit = grub_tolower (*str) - '0'; if (digit > 9) { digit += '0' - 'a' + 10; if (digit >= (unsigned long) base) break; } found = 1; /* NUM * BASE + DIGIT > ~0ULL */ if (num > grub_divmod64 (~0ULL - digit, base, 0)) { grub_error (GRUB_ERR_OUT_OF_RANGE, "overflow is detected"); return ~0ULL; } num = num * base + digit; str++; } if (! found) { grub_error (GRUB_ERR_BAD_NUMBER, "unrecognized number"); return 0; } if (end) *end = (char *) str; return num; }
p = str; if (base == 16) do { unsigned d = (unsigned) (n & 0xf); *p++ = (d > 9) ? d + 'a' - 10 : d + '0'; } while (n >>= 4); else /* BASE == 10 */ do { unsigned m; n = grub_divmod64 (n, 10, &m); *p++ = m + '0'; } while (n); *p = 0; grub_reverse (str); return p; } struct vsnprintf_closure { char *str; grub_size_t count; grub_size_t max_len;
static grub_err_t grub_ls_list_files (char *dirname, int longlist, int all, int human) { char *device_name; grub_fs_t fs; const char *path; grub_device_t dev; auto int print_files (const char *filename, const struct grub_dirhook_info *info); auto int print_files_long (const char *filename, const struct grub_dirhook_info *info); int print_files (const char *filename, const struct grub_dirhook_info *info) { if (all || filename[0] != '.') grub_printf ("%s%s ", filename, info->dir ? "/" : ""); return 0; } int print_files_long (const char *filename, const struct grub_dirhook_info *info) { if ((! all) && (filename[0] == '.')) return 0; if (! info->dir) { grub_file_t file; char *pathname; if (dirname[grub_strlen (dirname) - 1] == '/') pathname = grub_xasprintf ("%s%s", dirname, filename); else pathname = grub_xasprintf ("%s/%s", dirname, filename); if (!pathname) return 1; /* XXX: For ext2fs symlinks are detected as files while they should be reported as directories. */ grub_file_filter_disable_compression (); file = grub_file_open (pathname); if (! file) { grub_errno = 0; grub_free (pathname); return 0; } if (! human) grub_printf ("%-12llu", (unsigned long long) file->size); else { grub_uint64_t fsize = file->size * 100ULL; grub_uint64_t fsz = file->size; int units = 0; char buf[20]; while (fsz / 1024) { fsize = (fsize + 512) / 1024; fsz /= 1024; units++; } if (units) { grub_uint64_t whole, fraction; whole = grub_divmod64 (fsize, 100, &fraction); grub_snprintf (buf, sizeof (buf), "%" PRIuGRUB_UINT64_T ".%02" PRIuGRUB_UINT64_T "%c", whole, fraction, grub_human_sizes[units]); grub_printf ("%-12s", buf); } else grub_printf ("%-12llu", (unsigned long long) file->size); } grub_file_close (file); grub_free (pathname); } else grub_printf ("%-12s", _("DIR")); if (info->mtimeset) { struct grub_datetime datetime; grub_unixtime2datetime (info->mtime, &datetime); if (human) grub_printf (" %d-%02d-%02d %02d:%02d:%02d %-11s ", datetime.year, datetime.month, datetime.day, datetime.hour, datetime.minute, datetime.second, grub_get_weekday_name (&datetime)); else grub_printf (" %04d%02d%02d%02d%02d%02d ", datetime.year, datetime.month, datetime.day, datetime.hour, datetime.minute, datetime.second); } grub_printf ("%s%s\n", filename, info->dir ? "/" : ""); return 0; } device_name = grub_file_get_device_name (dirname); dev = grub_device_open (device_name); if (! dev) goto fail; fs = grub_fs_probe (dev); path = grub_strchr (dirname, ')'); if (! path) path = dirname; else path++; if (! path && ! device_name) { grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid argument"); goto fail; } if (! *path) { if (grub_errno == GRUB_ERR_UNKNOWN_FS) grub_errno = GRUB_ERR_NONE; grub_normal_print_device_info (device_name); } else if (fs) { if (longlist) (fs->dir) (dev, path, print_files_long); else (fs->dir) (dev, path, print_files); if (grub_errno == GRUB_ERR_BAD_FILE_TYPE && path[grub_strlen (path) - 1] != '/') { /* PATH might be a regular file. */ char *p; grub_file_t file; struct grub_dirhook_info info; grub_errno = 0; grub_file_filter_disable_compression (); file = grub_file_open (dirname); if (! file) goto fail; grub_file_close (file); p = grub_strrchr (dirname, '/') + 1; dirname = grub_strndup (dirname, p - dirname); if (! dirname) goto fail; all = 1; grub_memset (&info, 0, sizeof (info)); if (longlist) print_files_long (p, &info); else print_files (p, &info); grub_free (dirname); } if (grub_errno == GRUB_ERR_NONE) grub_xputs ("\n"); grub_refresh (); } fail: if (dev) grub_device_close (dev); grub_free (device_name); return 0; }
static grub_err_t grub_lvm_read (grub_disk_t disk, grub_disk_addr_t sector, grub_size_t size, char *buf) { grub_err_t err = 0; struct grub_lvm_lv *lv = disk->data; struct grub_lvm_vg *vg = lv->vg; struct grub_lvm_segment *seg = lv->segments; struct grub_lvm_pv *pv; grub_uint64_t offset; grub_uint64_t extent; unsigned int i; extent = grub_divmod64 (sector, vg->extent_size, NULL); /* Find the right segment. */ for (i = 0; i < lv->segment_count; i++) { if ((seg->start_extent <= extent) && ((seg->start_extent + seg->extent_count) > extent)) { break; } seg++; } if (seg->stripe_count == 1) { /* This segment is linear, so that's easy. We just need to find out the offset in the physical volume and read SIZE bytes from that. */ struct grub_lvm_stripe *stripe = seg->stripes; grub_uint64_t seg_offset; /* Offset of the segment in PV device. */ pv = stripe->pv; seg_offset = ((grub_uint64_t) stripe->start * (grub_uint64_t) vg->extent_size) + pv->start; offset = sector - ((grub_uint64_t) seg->start_extent * (grub_uint64_t) vg->extent_size) + seg_offset; } else { /* This is a striped segment. We have to find the right PV similar to RAID0. */ struct grub_lvm_stripe *stripe = seg->stripes; grub_uint32_t a, b; grub_uint64_t seg_offset; /* Offset of the segment in PV device. */ unsigned int stripenr; offset = sector - ((grub_uint64_t) seg->start_extent * (grub_uint64_t) vg->extent_size); a = grub_divmod64 (offset, seg->stripe_size, NULL); grub_divmod64 (a, seg->stripe_count, &stripenr); a = grub_divmod64 (offset, seg->stripe_size * seg->stripe_count, NULL); grub_divmod64 (offset, seg->stripe_size, &b); offset = a * seg->stripe_size + b; stripe += stripenr; pv = stripe->pv; seg_offset = ((grub_uint64_t) stripe->start * (grub_uint64_t) vg->extent_size) + pv->start; offset += seg_offset; } /* Check whether we actually know the physical volume we want to read from. */ if (pv->disk) err = grub_disk_read (pv->disk, offset, 0, size << GRUB_DISK_SECTOR_BITS, buf); else err = grub_error (GRUB_ERR_UNKNOWN_DEVICE, "physical volume %s not found", pv->name); return err; }
static grub_ssize_t grub_bufio_read (grub_file_t file, char *buf, grub_size_t len) { grub_size_t res = len; grub_bufio_t bufio = file->data; grub_uint32_t pos; if ((file->offset >= bufio->file->offset) && (file->offset < bufio->file->offset + bufio->buffer_len)) { grub_size_t n; pos = file->offset - bufio->file->offset; n = bufio->buffer_len - pos; if (n > len) n = len; grub_memcpy (buf, &bufio->buffer[pos], n); len -= n; if (! len) return res; buf += n; bufio->file->offset += bufio->buffer_len; pos = 0; } else { bufio->file->offset = grub_divmod64 (file->offset, bufio->block_size, &pos); bufio->file->offset *= bufio->block_size; } if (pos + len >= bufio->block_size) { if (pos) { grub_size_t n; bufio->file->fs->read (bufio->file, bufio->buffer, bufio->block_size); if (grub_errno) return -1; n = bufio->block_size - pos; grub_memcpy (buf, &bufio->buffer[pos], n); len -= n; buf += n; bufio->file->offset += bufio->block_size; pos = 0; } while (len >= bufio->block_size) { bufio->file->fs->read (bufio->file, buf, bufio->block_size); if (grub_errno) return -1; len -= bufio->block_size; buf += bufio->block_size; bufio->file->offset += bufio->block_size; } if (! len) { bufio->buffer_len = 0; return res; } } bufio->buffer_len = bufio->file->size - bufio->file->offset; if (bufio->buffer_len > bufio->block_size) bufio->buffer_len = bufio->block_size; bufio->file->fs->read (bufio->file, bufio->buffer, bufio->buffer_len); if (grub_errno) return -1; grub_memcpy (buf, &bufio->buffer[pos], len); return res; }