/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004,2006,2007,2008,2009,2010,2011,2012,2013 Free Software Foundation, Inc. * * 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 3 of the License, or * (at your option) any later version. * * GRUB 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, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define BOUNCE_SIZE 1048576 static ULONG *bounce; char * canonicalize_file_name (const char *path) { char *ret; BPTR lck; const char *p; p = strchr (path, ':'); if (p && !p[1]) return xstrdup (path); ret = xmalloc (2048); lck = Lock ((const unsigned char *) path, SHARED_LOCK); if (!lck || !NameFromLock (lck, (unsigned char *) ret, 2040)) { free (ret); ret = xstrdup (path); } if (lck) UnLock (lck); return ret; } static grub_uint64_t grub_util_get_fd_size_volume (grub_util_fd_t fd __attribute__ ((unused)), const char *dev, unsigned *log_secsize) { struct DriveGeometry *geo; LONG err; unsigned sector_size, log_sector_size; if (!bounce) bounce = AllocVec (BOUNCE_SIZE, MEMF_PUBLIC | MEMF_CLEAR); if (!bounce) grub_util_error ("out of memory"); fd->ioreq->iotd_Req.io_Command = TD_GETGEOMETRY; fd->ioreq->iotd_Req.io_Length = sizeof (*geo); fd->ioreq->iotd_Req.io_Data = bounce; fd->ioreq->iotd_Req.io_Offset = 0; fd->ioreq->iotd_Req.io_Actual = 0; err = DoIO ((struct IORequest *) fd->ioreq); if (err) { grub_util_info ("I/O failed with error %d, IoErr=%d", (int)err, (int) IoErr ()); return -1; } geo = (struct DriveGeometry *) bounce; sector_size = geo->dg_SectorSize; if (sector_size & (sector_size - 1) || !sector_size) return -1; for (log_sector_size = 0; (1 << log_sector_size) < sector_size; log_sector_size++); if (log_secsize) *log_secsize = log_sector_size; return (grub_uint64_t) geo->dg_TotalSectors * (grub_uint64_t) geo->dg_SectorSize; } static grub_uint64_t grub_util_get_fd_size_file (grub_util_fd_t fd, const char *dev __attribute__ ((unused)), unsigned *log_secsize) { off_t oo, ro; *log_secsize = 9; /* FIXME: support 64-bit offsets. */ oo = lseek (fd->fd, 0, SEEK_CUR); ro = lseek (fd->fd, 0, SEEK_END); lseek (fd->fd, oo, SEEK_SET); return ro; } grub_err_t grub_util_fd_seek (grub_util_fd_t fd, const char *name, grub_uint64_t off) { switch (fd->type) { case GRUB_UTIL_FD_FILE: if (lseek (fd->fd, 0, SEEK_SET) == (off_t) -1) return grub_error (GRUB_ERR_BAD_DEVICE, N_("cannot seek `%s': %s"), name, strerror (errno)); fd->off = off; return 0; case GRUB_UTIL_FD_DISK: fd->off = off; return 0; } return 0; } grub_util_fd_t grub_util_fd_open (const char *dev, int flg) { grub_util_fd_t ret = xmalloc (sizeof (*ret)); const char *p, *p1, *p2; char *tmp; IPTR unit = 0; ULONG flags = 0; #ifdef O_LARGEFILE flg |= O_LARGEFILE; #endif #ifdef O_BINARY flg |= O_BINARY; #endif ret->off = 0; if (dev[0] == '\0') { free (ret); return NULL; } p = strchr (dev, ':'); if (!p || p[1]) { ret->type = GRUB_UTIL_FD_FILE; ret->fd = open (dev, flg); if (ret->fd < 0) { free (ret); return NULL; } return ret; } p1 = strchr (dev, '/'); if (!p1) p1 = p; else { unit = grub_strtoul (p1 + 1, (char **) &p2, 16); if (p2 && *p2 == '/') flags = grub_strtoul (p2 + 1, 0, 16); } ret->mp = CreateMsgPort(); if (!ret->mp) { free (ret); return NULL; } ret->ioreq = (struct IOExtTD *) CreateIORequest(ret->mp, sizeof(struct IOExtTD)); if (!ret->ioreq) { free (ret); DeleteMsgPort (ret->mp); return NULL; } tmp = xmalloc (p1 - dev + 1); memcpy (tmp, dev, p1 - dev); tmp[p1 - dev] = '\0'; ret->type = GRUB_UTIL_FD_DISK; ret->is_floppy = (strcmp (tmp, TD_NAME) == 0); ret->is_64 = 1; if (OpenDevice ((unsigned char *) tmp, unit, (struct IORequest *) ret->ioreq, flags)) { free (tmp); free (ret); DeleteMsgPort (ret->mp); return NULL; } free (tmp); return ret; } static ssize_t grub_util_fd_read_file (grub_util_fd_t fd, char *buf, size_t len) { ssize_t size = len; while (len) { ssize_t ret = read (fd->fd, buf, len); if (ret <= 0) { if (errno == EINTR) continue; else return ret; } fd->off += ret; len -= ret; buf += ret; } return size; } /* Write LEN bytes from BUF to FD. Return less than or equal to zero if an error occurs, otherwise return LEN. */ static ssize_t grub_util_fd_write_file (grub_util_fd_t fd, const char *buf, size_t len) { ssize_t size = len; while (len) { ssize_t ret = write (fd->fd, buf, len); if (ret <= 0) { if (errno == EINTR) continue; else return ret; } fd->off += ret; len -= ret; buf += ret; } return size; } static void stop_motor (grub_util_fd_t fd) { if (!fd->is_floppy) return; fd->ioreq->iotd_Req.io_Command = TD_MOTOR; fd->ioreq->iotd_Req.io_Length = 0; fd->ioreq->iotd_Req.io_Data = 0; fd->ioreq->iotd_Req.io_Offset = 0; fd->ioreq->iotd_Req.io_Actual = 0; DoIO ((struct IORequest *) fd->ioreq); } static ssize_t grub_util_fd_read_volume (grub_util_fd_t fd, char *buf, size_t len) { grub_uint64_t adj = 0; if (!bounce) bounce = AllocVec (BOUNCE_SIZE, MEMF_PUBLIC | MEMF_CLEAR); if (!bounce) grub_util_error ("out of memory"); while (len) { size_t cr = len; LONG err; if (cr > BOUNCE_SIZE) cr = BOUNCE_SIZE; retry: if (fd->is_64) fd->ioreq->iotd_Req.io_Command = NSCMD_TD_READ64; else fd->ioreq->iotd_Req.io_Command = CMD_READ; fd->ioreq->iotd_Req.io_Length = cr; fd->ioreq->iotd_Req.io_Data = bounce; fd->ioreq->iotd_Req.io_Offset = fd->off & 0xFFFFFFFF; fd->ioreq->iotd_Req.io_Actual = fd->off >> 32; err = DoIO ((struct IORequest *) fd->ioreq); if (err == IOERR_NOCMD && fd->is_64) { fd->is_64 = 0; goto retry; } if (err) { grub_util_info ("I/O failed with error %d, IoErr=%d", (int)err, (int) IoErr ()); stop_motor (fd); return -1; } memcpy (buf, bounce, cr); adj += cr; len -= cr; buf += cr; } fd->off += adj; stop_motor (fd); return adj; } static ssize_t grub_util_fd_write_volume (grub_util_fd_t fd, const char *buf, size_t len) { grub_uint64_t adj = 0; if (!bounce) bounce = AllocVec (BOUNCE_SIZE, MEMF_PUBLIC | MEMF_CLEAR); if (!bounce) grub_util_error ("out of memory"); while (len) { size_t cr = len; LONG err; if (cr > BOUNCE_SIZE) cr = BOUNCE_SIZE; retry: if (fd->is_64) fd->ioreq->iotd_Req.io_Command = NSCMD_TD_WRITE64; else fd->ioreq->iotd_Req.io_Command = CMD_WRITE; fd->ioreq->iotd_Req.io_Length = cr; fd->ioreq->iotd_Req.io_Data = bounce; fd->ioreq->iotd_Req.io_Offset = fd->off & 0xFFFFFFFF; fd->ioreq->iotd_Req.io_Actual = fd->off >> 32; memcpy (bounce, buf, cr); err = DoIO ((struct IORequest *) fd->ioreq); if (err == IOERR_NOCMD && fd->is_64) { fd->is_64 = 0; goto retry; } if (err) { grub_util_info ("I/O failed with error %d", err); stop_motor (fd); return -1; } adj += cr; len -= cr; buf += cr; } fd->off += adj; stop_motor (fd); return adj; } ssize_t grub_util_fd_read (grub_util_fd_t fd, char *buf, size_t len) { switch (fd->type) { case GRUB_UTIL_FD_FILE: return grub_util_fd_read_file (fd, buf, len); case GRUB_UTIL_FD_DISK: return grub_util_fd_read_volume (fd, buf, len); } return -1; } ssize_t grub_util_fd_write (grub_util_fd_t fd, const char *buf, size_t len) { switch (fd->type) { case GRUB_UTIL_FD_FILE: return grub_util_fd_write_file (fd, buf, len); case GRUB_UTIL_FD_DISK: return grub_util_fd_write_volume (fd, buf, len); } return -1; } grub_uint64_t grub_util_get_fd_size (grub_util_fd_t fd, const char *dev, unsigned *log_secsize) { switch (fd->type) { case GRUB_UTIL_FD_FILE: return grub_util_get_fd_size_file (fd, dev, log_secsize); case GRUB_UTIL_FD_DISK: return grub_util_get_fd_size_volume (fd, dev, log_secsize); } return -1; } void grub_util_fd_close (grub_util_fd_t fd) { switch (fd->type) { case GRUB_UTIL_FD_FILE: close (fd->fd); return; case GRUB_UTIL_FD_DISK: CloseDevice ((struct IORequest *) fd->ioreq); DeleteIORequest((struct IORequest *) fd->ioreq); DeleteMsgPort (fd->mp); return; } } static void grub_util_fd_sync_volume (grub_util_fd_t fd) { fd->ioreq->iotd_Req.io_Command = CMD_UPDATE; fd->ioreq->iotd_Req.io_Length = 0; fd->ioreq->iotd_Req.io_Data = 0; fd->ioreq->iotd_Req.io_Offset = 0; fd->ioreq->iotd_Req.io_Actual = 0; DoIO ((struct IORequest *) fd->ioreq); } void grub_util_fd_sync (grub_util_fd_t fd) { switch (fd->type) { case GRUB_UTIL_FD_FILE: fsync (fd->fd); return; case GRUB_UTIL_FD_DISK: grub_util_fd_sync_volume (fd); return; } } void grub_hostdisk_flush_initial_buffer (const char *os_dev __attribute__ ((unused))) { } const char * grub_util_fd_strerror (void) { static char buf[201]; LONG err = IoErr (); if (!err) return _("Success"); memset (buf, '\0', sizeof (buf)); Fault (err, (const unsigned char *) "", (STRPTR) buf, sizeof (buf)); if (buf[0] == ':') return buf + 1; return buf; }