306 lines
6.0 KiB
C
306 lines
6.0 KiB
C
/* ----------------------------------------------------------------------- *
|
|
*
|
|
* Copyright 2010 Intel Corp. - All Rights Reserved
|
|
*
|
|
* This program 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, Inc., 53 Temple Place Ste 330,
|
|
* Boston MA 02111-1307, USA; either version 2 of the License, or
|
|
* (at your option) any later version; incorporated herein by reference.
|
|
*
|
|
* ----------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* syslxcom.c
|
|
*
|
|
* common functions for extlinux & syslinux installer
|
|
*
|
|
*/
|
|
#define _GNU_SOURCE
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <getopt.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/vfs.h>
|
|
|
|
#include "linuxioctl.h"
|
|
#include "syslxcom.h"
|
|
#include "syslxfs.h"
|
|
|
|
const char *program;
|
|
|
|
int fs_type;
|
|
|
|
#ifdef DEBUG
|
|
# define dprintf printf
|
|
#else
|
|
# define dprintf(...) ((void)0)
|
|
#endif
|
|
|
|
#define SECTOR_SHIFT 9
|
|
|
|
static void die(const char *msg)
|
|
{
|
|
fputs(msg, stderr);
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* read/write wrapper functions
|
|
*/
|
|
ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
|
|
{
|
|
char *bufp = (char *)buf;
|
|
ssize_t rv;
|
|
ssize_t done = 0;
|
|
|
|
while (count) {
|
|
rv = pread(fd, bufp, count, offset);
|
|
if (rv == 0) {
|
|
die("short read");
|
|
} else if (rv == -1) {
|
|
if (errno == EINTR) {
|
|
continue;
|
|
} else {
|
|
die(strerror(errno));
|
|
}
|
|
} else {
|
|
bufp += rv;
|
|
offset += rv;
|
|
done += rv;
|
|
count -= rv;
|
|
}
|
|
}
|
|
|
|
return done;
|
|
}
|
|
|
|
ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset)
|
|
{
|
|
const char *bufp = (const char *)buf;
|
|
ssize_t rv;
|
|
ssize_t done = 0;
|
|
|
|
while (count) {
|
|
rv = pwrite(fd, bufp, count, offset);
|
|
if (rv == 0) {
|
|
die("short write");
|
|
} else if (rv == -1) {
|
|
if (errno == EINTR) {
|
|
continue;
|
|
} else {
|
|
die(strerror(errno));
|
|
}
|
|
} else {
|
|
bufp += rv;
|
|
offset += rv;
|
|
done += rv;
|
|
count -= rv;
|
|
}
|
|
}
|
|
|
|
return done;
|
|
}
|
|
|
|
/*
|
|
* Set and clear file attributes
|
|
*/
|
|
void clear_attributes(int fd)
|
|
{
|
|
struct stat st;
|
|
|
|
if (!fstat(fd, &st)) {
|
|
switch (fs_type) {
|
|
case EXT2:
|
|
{
|
|
int flags;
|
|
|
|
if (!ioctl(fd, FS_IOC_GETFLAGS, &flags)) {
|
|
flags &= ~FS_IMMUTABLE_FL;
|
|
ioctl(fd, FS_IOC_SETFLAGS, &flags);
|
|
}
|
|
break;
|
|
}
|
|
case VFAT:
|
|
{
|
|
uint32_t attr = 0x00; /* Clear all attributes */
|
|
ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr);
|
|
break;
|
|
}
|
|
case NTFS:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
fchmod(fd, st.st_mode | S_IWUSR);
|
|
}
|
|
}
|
|
|
|
void set_attributes(int fd)
|
|
{
|
|
struct stat st;
|
|
|
|
if (!fstat(fd, &st)) {
|
|
fchmod(fd, st.st_mode & (S_IRUSR | S_IRGRP | S_IROTH));
|
|
switch (fs_type) {
|
|
case EXT2:
|
|
{
|
|
int flags;
|
|
|
|
if (st.st_uid == 0 && !ioctl(fd, FS_IOC_GETFLAGS, &flags)) {
|
|
flags |= FS_IMMUTABLE_FL;
|
|
ioctl(fd, FS_IOC_SETFLAGS, &flags);
|
|
}
|
|
break;
|
|
}
|
|
case VFAT:
|
|
{
|
|
uint32_t attr = 0x07; /* Hidden+System+Readonly */
|
|
ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr);
|
|
break;
|
|
}
|
|
case NTFS:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* New FIEMAP based mapping */
|
|
static int sectmap_fie(int fd, sector_t *sectors, int nsectors)
|
|
{
|
|
struct fiemap *fm;
|
|
struct fiemap_extent *fe;
|
|
unsigned int i, nsec;
|
|
sector_t sec, *secp, *esec;
|
|
struct stat st;
|
|
uint64_t maplen;
|
|
|
|
if (fstat(fd, &st))
|
|
return -1;
|
|
|
|
fm = alloca(sizeof(struct fiemap)
|
|
+ nsectors * sizeof(struct fiemap_extent));
|
|
|
|
memset(fm, 0, sizeof *fm);
|
|
|
|
maplen = (uint64_t)nsectors << SECTOR_SHIFT;
|
|
if (maplen > (uint64_t)st.st_size)
|
|
maplen = st.st_size;
|
|
|
|
fm->fm_start = 0;
|
|
fm->fm_length = maplen;
|
|
fm->fm_flags = FIEMAP_FLAG_SYNC;
|
|
fm->fm_extent_count = nsectors;
|
|
|
|
if (ioctl(fd, FS_IOC_FIEMAP, fm))
|
|
return -1;
|
|
|
|
memset(sectors, 0, nsectors * sizeof *sectors);
|
|
esec = sectors + nsectors;
|
|
|
|
fe = fm->fm_extents;
|
|
|
|
if (fm->fm_mapped_extents < 1 ||
|
|
!(fe[fm->fm_mapped_extents-1].fe_flags & FIEMAP_EXTENT_LAST))
|
|
return -1;
|
|
|
|
for (i = 0; i < fm->fm_mapped_extents; i++) {
|
|
if (fe->fe_flags & FIEMAP_EXTENT_LAST) {
|
|
/* If this is the *final* extent, pad the length */
|
|
fe->fe_length = (fe->fe_length + SECTOR_SIZE - 1)
|
|
& ~(SECTOR_SIZE - 1);
|
|
}
|
|
|
|
if ((fe->fe_logical | fe->fe_physical| fe->fe_length) &
|
|
(SECTOR_SIZE - 1))
|
|
return -1;
|
|
|
|
if (fe->fe_flags & (FIEMAP_EXTENT_UNKNOWN|
|
|
FIEMAP_EXTENT_DELALLOC|
|
|
FIEMAP_EXTENT_ENCODED|
|
|
FIEMAP_EXTENT_DATA_ENCRYPTED|
|
|
FIEMAP_EXTENT_UNWRITTEN))
|
|
return -1;
|
|
|
|
secp = sectors + (fe->fe_logical >> SECTOR_SHIFT);
|
|
sec = fe->fe_physical >> SECTOR_SHIFT;
|
|
nsec = fe->fe_length >> SECTOR_SHIFT;
|
|
|
|
while (nsec--) {
|
|
if (secp >= esec)
|
|
break;
|
|
*secp++ = sec++;
|
|
}
|
|
|
|
fe++;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Legacy FIBMAP based mapping */
|
|
static int sectmap_fib(int fd, sector_t *sectors, int nsectors)
|
|
{
|
|
unsigned int blk, nblk;
|
|
unsigned int i;
|
|
unsigned int blksize;
|
|
sector_t sec;
|
|
|
|
/* Get block size */
|
|
if (ioctl(fd, FIGETBSZ, &blksize))
|
|
return -1;
|
|
|
|
/* Number of sectors per block */
|
|
blksize >>= SECTOR_SHIFT;
|
|
|
|
nblk = 0;
|
|
while (nsectors) {
|
|
blk = nblk++;
|
|
if (ioctl(fd, FIBMAP, &blk))
|
|
return -1;
|
|
|
|
sec = (sector_t)blk * blksize;
|
|
for (i = 0; i < blksize; i++) {
|
|
*sectors++ = sec++;
|
|
if (! --nsectors)
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Produce file map
|
|
*/
|
|
int sectmap(int fd, sector_t *sectors, int nsectors)
|
|
{
|
|
if (!sectmap_fie(fd, sectors, nsectors))
|
|
return 0;
|
|
|
|
return sectmap_fib(fd, sectors, nsectors);
|
|
}
|
|
|
|
/*
|
|
* SYSLINUX installs the string 'SYSLINUX' at offset 3 in the boot
|
|
* sector; this is consistent with FAT filesystems. Earlier versions
|
|
* would install the string "EXTLINUX" instead, handle both.
|
|
*/
|
|
int syslinux_already_installed(int dev_fd)
|
|
{
|
|
char buffer[8];
|
|
|
|
xpread(dev_fd, buffer, 8, 3);
|
|
return !memcmp(buffer, "SYSLINUX", 8) || !memcmp(buffer, "EXTLINUX", 8);
|
|
}
|