Merge branch 'pathbased' of ssh://terminus.zytor.com/pub/git/syslinux/syslinux into...
[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/ioctl.h>
30 #include <sys/stat.h>
31 #include <sys/types.h>
32 #include <sys/mount.h>
33 #include <sys/vfs.h>
34 #include <linux/fs.h>           /* FIGETBSZ, FIBMAP */
35 #include <linux/msdos_fs.h>     /* FAT_IOCTL_SET_ATTRIBUTES */
36 #undef SECTOR_SIZE              /* Defined in msdos_fs.h for no good reason */
37 #include <linux/fiemap.h>       /* FIEMAP definitions */
38 #include "syslxcom.h"
39
40 const char *program;
41
42 int fs_type;
43
44 #ifdef DEBUG
45 # define dprintf printf
46 #else
47 # define dprintf(...) ((void)0)
48 #endif
49
50 #define SECTOR_SHIFT    9
51 #define EXT2_IMMUTABLE_FL               0x00000010      /* Immutable file */
52
53 /*
54  * ioctl commands
55  */
56 #define EXT2_IOC_GETFLAGS               _IOR('f', 1, long)
57 #define EXT2_IOC_SETFLAGS               _IOW('f', 2, long)
58 #define EXT2_IOC_GETVERSION             _IOR('v', 1, long)
59 #define EXT2_IOC_SETVERSION             _IOW('v', 2, long)
60
61 static void die(const char *msg)
62 {
63     fputs(msg, stderr);
64     exit(1);
65 }
66
67 /*
68  * read/write wrapper functions
69  */
70 ssize_t xpread(int fd, void *buf, size_t count, off_t offset)
71 {
72     char *bufp = (char *)buf;
73     ssize_t rv;
74     ssize_t done = 0;
75
76     while (count) {
77         rv = pread(fd, bufp, count, offset);
78         if (rv == 0) {
79             die("short read");
80         } else if (rv == -1) {
81             if (errno == EINTR) {
82                 continue;
83             } else {
84                 die(strerror(errno));
85             }
86         } else {
87             bufp += rv;
88             offset += rv;
89             done += rv;
90             count -= rv;
91         }
92     }
93
94     return done;
95 }
96
97 ssize_t xpwrite(int fd, const void *buf, size_t count, off_t offset)
98 {
99     const char *bufp = (const char *)buf;
100     ssize_t rv;
101     ssize_t done = 0;
102
103     while (count) {
104         rv = pwrite(fd, bufp, count, offset);
105         if (rv == 0) {
106             die("short write");
107         } else if (rv == -1) {
108             if (errno == EINTR) {
109                 continue;
110             } else {
111                 die(strerror(errno));
112             }
113         } else {
114             bufp += rv;
115             offset += rv;
116             done += rv;
117             count -= rv;
118         }
119     }
120
121     return done;
122 }
123
124 /*
125  * Set and clear file attributes
126  */
127 void clear_attributes(int fd)
128 {
129     struct stat st;
130
131     if (!fstat(fd, &st)) {
132         switch (fs_type) {
133         case EXT2:
134         {
135             int flags;
136
137             if (!ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
138                 flags &= ~EXT2_IMMUTABLE_FL;
139                 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
140             }
141             break;
142         }
143         case VFAT:
144         {
145             uint32_t attr = 0x00; /* Clear all attributes */
146             ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr);
147             break;
148         }
149         default:
150             break;
151         }
152         fchmod(fd, st.st_mode | S_IWUSR);
153     }
154 }
155
156 void set_attributes(int fd)
157 {
158     struct stat st;
159
160     if (!fstat(fd, &st)) {
161         fchmod(fd, st.st_mode & (S_IRUSR | S_IRGRP | S_IROTH));
162         switch (fs_type) {
163         case EXT2:
164         {
165             int flags;
166
167             if (st.st_uid == 0 && !ioctl(fd, EXT2_IOC_GETFLAGS, &flags)) {
168                 flags |= EXT2_IMMUTABLE_FL;
169                 ioctl(fd, EXT2_IOC_SETFLAGS, &flags);
170             }
171             break;
172         }
173         case VFAT:
174         {
175             uint32_t attr = 0x07; /* Hidden+System+Readonly */
176             ioctl(fd, FAT_IOCTL_SET_ATTRIBUTES, &attr);
177             break;
178         }
179         default:
180             break;
181         }
182     }
183 }
184
185 /* New FIEMAP based mapping */
186 static int sectmap_fie(int fd, sector_t *sectors, int nsectors)
187 {
188     struct fiemap *fm;
189     struct fiemap_extent *fe;
190     unsigned int i, nsec;
191     sector_t sec, *secp, *esec;
192     struct stat st;
193     uint64_t maplen;
194
195     if (fstat(fd, &st))
196         return -1;
197
198     fm = alloca(sizeof(struct fiemap)
199                 + nsectors * sizeof(struct fiemap_extent));
200
201     memset(fm, 0, sizeof *fm);
202
203     maplen = (uint64_t)nsectors << SECTOR_SHIFT;
204     if (maplen > (uint64_t)st.st_size)
205         maplen = st.st_size;
206
207     fm->fm_start        = 0;
208     fm->fm_length       = maplen;
209     fm->fm_flags        = FIEMAP_FLAG_SYNC;
210     fm->fm_extent_count = nsectors;
211
212     if (ioctl(fd, FS_IOC_FIEMAP, &fm))
213         return -1;
214
215     memset(sectors, 0, nsectors * sizeof *sectors);
216     esec = sectors + nsectors;
217
218     fe = fm->fm_extents;
219
220     if (fm->fm_mapped_extents < 1 ||
221         !(fe[fm->fm_mapped_extents-1].fe_flags & FIEMAP_EXTENT_LAST))
222         return -1;
223
224     for (i = 0; i < fm->fm_mapped_extents; i++) {
225         if (fe->fe_flags & FIEMAP_EXTENT_LAST) {
226             /* If this is the *final* extent, pad the length */
227             fe->fe_length = (fe->fe_length + SECTOR_SIZE - 1)
228                 & (SECTOR_SIZE - 1);
229         }
230
231         if ((fe->fe_logical | fe->fe_physical| fe->fe_length) &
232             (SECTOR_SIZE - 1))
233             return -1;
234
235         if (fe->fe_flags & (FIEMAP_EXTENT_UNKNOWN|
236                             FIEMAP_EXTENT_DELALLOC|
237                             FIEMAP_EXTENT_ENCODED|
238                             FIEMAP_EXTENT_DATA_ENCRYPTED|
239                             FIEMAP_EXTENT_UNWRITTEN))
240             return -1;
241
242         secp = sectors + (fe->fe_logical >> SECTOR_SHIFT);
243         sec  = fe->fe_physical >> SECTOR_SHIFT;
244         nsec = fe->fe_length >> SECTOR_SHIFT;
245
246         while (nsec--) {
247             if (secp >= esec)
248                 break;
249             *secp++ = sec++;
250         }
251
252         fe++;
253     }
254
255     return 0;
256 }
257
258 /* Legacy FIBMAP based mapping */
259 static int sectmap_fib(int fd, sector_t *sectors, int nsectors)
260 {
261     unsigned int blk, nblk;
262     unsigned int i;
263     unsigned int blksize;
264     sector_t sec;
265
266     /* Get block size */
267     if (ioctl(fd, FIGETBSZ, &blksize))
268         return -1;
269
270     /* Number of sectors per block */
271     blksize >>= SECTOR_SHIFT;
272
273     nblk = 0;
274     while (nsectors) {
275         blk = nblk++;
276         if (ioctl(fd, FIBMAP, &blk))
277             return -1;
278
279         sec = (sector_t)blk * blksize;
280         for (i = 0; i < blksize; i++) {
281             *sectors++ = sec++;
282             if (! --nsectors)
283                 break;
284         }
285     }
286
287     return 0;
288 }
289
290 /*
291  * Produce file map
292  */
293 int sectmap(int fd, sector_t *sectors, int nsectors)
294 {
295     if (!sectmap_fie(fd, sectors, nsectors))
296         return 0;
297
298     return sectmap_fib(fd, sectors, nsectors);
299 }