win: Add -mno-ms-bitfields
[profile/ivi/syslinux.git] / libinstaller / syslxcom.c
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
50 static void die(const char *msg)
51 {
52     fputs(msg, stderr);
53     exit(1);
54 }
55
56 /*
57  * read/write wrapper functions
58  */
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
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  */
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
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 */
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 */
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  */
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  */
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 }