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