summaryrefslogtreecommitdiffstats
path: root/contrib/syslinux-4.02/libinstaller/syslxcom.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/syslinux-4.02/libinstaller/syslxcom.c')
-rw-r--r--contrib/syslinux-4.02/libinstaller/syslxcom.c286
1 files changed, 286 insertions, 0 deletions
diff --git a/contrib/syslinux-4.02/libinstaller/syslxcom.c b/contrib/syslinux-4.02/libinstaller/syslxcom.c
new file mode 100644
index 0000000..b176f6d
--- /dev/null
+++ b/contrib/syslinux-4.02/libinstaller/syslxcom.c
@@ -0,0 +1,286 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * 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"
+
+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, EXT2_IOC_GETFLAGS, &flags)) {
+ flags &= ~EXT2_IMMUTABLE_FL;
+ ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
+ }
+ break;
+ }
+ case VFAT:
+ {
+ uint32_t attr = 0x00; /* Clear all attributes */
+ ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr);
+ 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, EXT2_IOC_GETFLAGS, &flags)) {
+ flags |= EXT2_IMMUTABLE_FL;
+ ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
+ }
+ break;
+ }
+ case VFAT:
+ {
+ uint32_t attr = 0x07; /* Hidden+System+Readonly */
+ ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr);
+ 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);
+}