/* ----------------------------------------------------------------------- * * * 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 #include #include #include #include #include #include #include #include #include #include #include #include "linuxioctl.h" #include "syslxrw.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 /* * 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); }