• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2010 Intel Corp. - All Rights Reserved
4  *
5  *   This program is free software; you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8  *   Boston MA 02111-1307, USA; either version 2 of the License, or
9  *   (at your option) any later version; incorporated herein by reference.
10  *
11  * ----------------------------------------------------------------------- */
12 
13 /*
14  * syslxcom.c
15  *
16  * common functions for extlinux & syslinux installer
17  *
18  */
19 #define  _GNU_SOURCE
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stddef.h>
24 #include <stdint.h>
25 #include <string.h>
26 #include <getopt.h>
27 #include <unistd.h>
28 #include <errno.h>
29 #include <sys/stat.h>
30 #include <sys/types.h>
31 #include <sys/mount.h>
32 #include <sys/vfs.h>
33 
34 #include "linuxioctl.h"
35 #include "syslxcom.h"
36 #include "syslxfs.h"
37 
38 const char *program;
39 
40 int fs_type;
41 
42 #ifdef DEBUG
43 # define dprintf printf
44 #else
45 # define dprintf(...) ((void)0)
46 #endif
47 
48 #define SECTOR_SHIFT	9
49 
die(const char * msg)50 static void die(const char *msg)
51 {
52     fputs(msg, stderr);
53     exit(1);
54 }
55 
56 /*
57  * read/write wrapper functions
58  */
xpread(int fd,void * buf,size_t count,off_t offset)59 ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
60 {
61     char *bufp = (char *)buf;
62     ssize_t rv;
63     ssize_t done = 0;
64 
65     while (count) {
66 	rv = pread(fd, bufp, count, offset);
67 	if (rv == 0) {
68 	    die("short read");
69 	} else if (rv == -1) {
70 	    if (errno == EINTR) {
71 		continue;
72 	    } else {
73 		die(strerror(errno));
74 	    }
75 	} else {
76 	    bufp += rv;
77 	    offset += rv;
78 	    done += rv;
79 	    count -= rv;
80 	}
81     }
82 
83     return done;
84 }
85 
xpwrite(int fd,const void * buf,size_t count,off_t offset)86 ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset)
87 {
88     const char *bufp = (const char *)buf;
89     ssize_t rv;
90     ssize_t done = 0;
91 
92     while (count) {
93 	rv = pwrite(fd, bufp, count, offset);
94 	if (rv == 0) {
95 	    die("short write");
96 	} else if (rv == -1) {
97 	    if (errno == EINTR) {
98 		continue;
99 	    } else {
100 		die(strerror(errno));
101 	    }
102 	} else {
103 	    bufp += rv;
104 	    offset += rv;
105 	    done += rv;
106 	    count -= rv;
107 	}
108     }
109 
110     return done;
111 }
112 
113 /*
114  * Set and clear file attributes
115  */
clear_attributes(int fd)116 void clear_attributes(int fd)
117 {
118     struct stat st;
119 
120     if (!fstat(fd, &st)) {
121 	switch (fs_type) {
122 	case EXT2:
123 	{
124 	    int flags;
125 
126 	    if (!ioctl(fd, FS_IOC_GETFLAGS, &flags)) {
127 		flags &= ~FS_IMMUTABLE_FL;
128 		ioctl(fd, FS_IOC_SETFLAGS, &flags);
129 	    }
130 	    break;
131 	}
132 	case VFAT:
133 	{
134 	    uint32_t attr = 0x00; /* Clear all attributes */
135 	    ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr);
136 	    break;
137 	}
138     case NTFS:
139         break;
140 	default:
141 	    break;
142 	}
143 	fchmod(fd, st.st_mode | S_IWUSR);
144     }
145 }
146 
set_attributes(int fd)147 void set_attributes(int fd)
148 {
149     struct stat st;
150 
151     if (!fstat(fd, &st)) {
152 	fchmod(fd, st.st_mode & (S_IRUSR | S_IRGRP | S_IROTH));
153 	switch (fs_type) {
154 	case EXT2:
155 	{
156 	    int flags;
157 
158 	    if (st.st_uid == 0 && !ioctl(fd, FS_IOC_GETFLAGS, &flags)) {
159 		flags |= FS_IMMUTABLE_FL;
160 		ioctl(fd, FS_IOC_SETFLAGS, &flags);
161 	    }
162 	    break;
163 	}
164 	case VFAT:
165 	{
166 	    uint32_t attr = 0x07; /* Hidden+System+Readonly */
167 	    ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr);
168 	    break;
169 	}
170     case NTFS:
171         break;
172 	default:
173 	    break;
174 	}
175     }
176 }
177 
178 /* New FIEMAP based mapping */
sectmap_fie(int fd,sector_t * sectors,int nsectors)179 static int sectmap_fie(int fd, sector_t *sectors, int nsectors)
180 {
181     struct fiemap *fm;
182     struct fiemap_extent *fe;
183     unsigned int i, nsec;
184     sector_t sec, *secp, *esec;
185     struct stat st;
186     uint64_t maplen;
187 
188     if (fstat(fd, &st))
189 	return -1;
190 
191     fm = alloca(sizeof(struct fiemap)
192 		+ nsectors * sizeof(struct fiemap_extent));
193 
194     memset(fm, 0, sizeof *fm);
195 
196     maplen = (uint64_t)nsectors << SECTOR_SHIFT;
197     if (maplen > (uint64_t)st.st_size)
198 	maplen = st.st_size;
199 
200     fm->fm_start        = 0;
201     fm->fm_length       = maplen;
202     fm->fm_flags        = FIEMAP_FLAG_SYNC;
203     fm->fm_extent_count = nsectors;
204 
205     if (ioctl(fd, FS_IOC_FIEMAP, fm))
206 	return -1;
207 
208     memset(sectors, 0, nsectors * sizeof *sectors);
209     esec = sectors + nsectors;
210 
211     fe = fm->fm_extents;
212 
213     if (fm->fm_mapped_extents < 1 ||
214 	!(fe[fm->fm_mapped_extents-1].fe_flags & FIEMAP_EXTENT_LAST))
215 	return -1;
216 
217     for (i = 0; i < fm->fm_mapped_extents; i++) {
218 	if (fe->fe_flags & FIEMAP_EXTENT_LAST) {
219 	    /* If this is the *final* extent, pad the length */
220 	    fe->fe_length = (fe->fe_length + SECTOR_SIZE - 1)
221 		& ~(SECTOR_SIZE - 1);
222 	}
223 
224 	if ((fe->fe_logical | fe->fe_physical| fe->fe_length) &
225 	    (SECTOR_SIZE - 1))
226 	    return -1;
227 
228 	if (fe->fe_flags & (FIEMAP_EXTENT_UNKNOWN|
229 			    FIEMAP_EXTENT_DELALLOC|
230 			    FIEMAP_EXTENT_ENCODED|
231 			    FIEMAP_EXTENT_DATA_ENCRYPTED|
232 			    FIEMAP_EXTENT_UNWRITTEN))
233 	    return -1;
234 
235 	secp = sectors + (fe->fe_logical >> SECTOR_SHIFT);
236 	sec  = fe->fe_physical >> SECTOR_SHIFT;
237 	nsec = fe->fe_length >> SECTOR_SHIFT;
238 
239 	while (nsec--) {
240 	    if (secp >= esec)
241 		break;
242 	    *secp++ = sec++;
243 	}
244 
245 	fe++;
246     }
247 
248     return 0;
249 }
250 
251 /* Legacy FIBMAP based mapping */
sectmap_fib(int fd,sector_t * sectors,int nsectors)252 static int sectmap_fib(int fd, sector_t *sectors, int nsectors)
253 {
254     unsigned int blk, nblk;
255     unsigned int i;
256     unsigned int blksize;
257     sector_t sec;
258 
259     /* Get block size */
260     if (ioctl(fd, FIGETBSZ, &blksize))
261 	return -1;
262 
263     /* Number of sectors per block */
264     blksize >>= SECTOR_SHIFT;
265 
266     nblk = 0;
267     while (nsectors) {
268 	blk = nblk++;
269 	if (ioctl(fd, FIBMAP, &blk))
270 	    return -1;
271 
272 	sec = (sector_t)blk * blksize;
273 	for (i = 0; i < blksize; i++) {
274 	    *sectors++ = sec++;
275 	    if (! --nsectors)
276 		break;
277 	}
278     }
279 
280     return 0;
281 }
282 
283 /*
284  * Produce file map
285  */
sectmap(int fd,sector_t * sectors,int nsectors)286 int sectmap(int fd, sector_t *sectors, int nsectors)
287 {
288     if (!sectmap_fie(fd, sectors, nsectors))
289 	return 0;
290 
291     return sectmap_fib(fd, sectors, nsectors);
292 }
293 
294 /*
295  * SYSLINUX installs the string 'SYSLINUX' at offset 3 in the boot
296  * sector; this is consistent with FAT filesystems.  Earlier versions
297  * would install the string "EXTLINUX" instead, handle both.
298  */
syslinux_already_installed(int dev_fd)299 int syslinux_already_installed(int dev_fd)
300 {
301     char buffer[8];
302 
303     xpread(dev_fd, buffer, 8, 3);
304     return !memcmp(buffer, "SYSLINUX", 8) || !memcmp(buffer, "EXTLINUX", 8);
305 }
306