Move modify_adv() into common code
[profile/ivi/syslinux.git] / libinstaller / advio.c
1 /* ----------------------------------------------------------------------- *
2  *
3  *   Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
4  *   Copyright 2010 Intel Corporation; author: H. Peter Anvin
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9  *   Boston MA 02111-1307, USA; either version 2 of the License, or
10  *   (at your option) any later version; incorporated herein by reference.
11  *
12  * ----------------------------------------------------------------------- */
13
14 /*
15  * advio.c
16  *
17  * Linux ADV I/O
18  *
19  * Return 0 on success, -1 on error, and set errno.
20  *
21  */
22 #define  _GNU_SOURCE
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stddef.h>
27 #include <stdint.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include "syslxint.h"
35 #include "syslxcom.h"
36
37 /*
38  * Read the ADV from an existing instance, or initialize if invalid.
39  * Returns -1 on fatal errors, 0 if ADV is okay, and 1 if no valid
40  * ADV was found.
41  */
42 int read_adv(const char *path, const char *cfg)
43 {
44     char *file;
45     int fd = -1;
46     struct stat st;
47     int err = 0;
48
49     asprintf(&file, "%s%s%s",
50              path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/", cfg);
51
52     if (!file) {
53         perror(program);
54         return -1;
55     }
56
57     fd = open(file, O_RDONLY);
58     if (fd < 0) {
59         if (errno != ENOENT) {
60             err = -1;
61         } else {
62             syslinux_reset_adv(syslinux_adv);
63         }
64     } else if (fstat(fd, &st)) {
65         err = -1;
66     } else if (st.st_size < 2 * ADV_SIZE) {
67         /* Too small to be useful */
68         syslinux_reset_adv(syslinux_adv);
69         err = 0;                /* Nothing to read... */
70     } else if (xpread(fd, syslinux_adv, 2 * ADV_SIZE,
71                       st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
72         err = -1;
73     } else {
74         /* We got it... maybe? */
75         err = syslinux_validate_adv(syslinux_adv) ? 1 : 0;
76     }
77
78     if (err < 0)
79         perror(file);
80
81     if (fd >= 0)
82         close(fd);
83
84     free(file);
85
86     return err;
87 }
88
89 /*
90  * Update the ADV in an existing installation.
91  */
92 int write_adv(const char *path, const char *cfg)
93 {
94     unsigned char advtmp[2 * ADV_SIZE];
95     char *file;
96     int fd = -1;
97     struct stat st, xst;
98     int err = 0;
99
100     err = asprintf(&file, "%s%s%s",
101         path, path[0] && path[strlen(path) - 1] == '/' ? "" : "/", cfg);
102
103     if (!file) {
104         perror(program);
105         return -1;
106     }
107
108     fd = open(file, O_RDONLY);
109     if (fd < 0) {
110         err = -1;
111     } else if (fstat(fd, &st)) {
112         err = -1;
113     } else if (st.st_size < 2 * ADV_SIZE) {
114         /* Too small to be useful */
115         err = -2;
116     } else if (xpread(fd, advtmp, 2 * ADV_SIZE,
117                       st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
118         err = -1;
119     } else {
120         /* We got it... maybe? */
121         err = syslinux_validate_adv(advtmp) ? -2 : 0;
122         if (!err) {
123             /* Got a good one, write our own ADV here */
124             clear_attributes(fd);
125
126             /* Need to re-open read-write */
127             close(fd);
128             fd = open(file, O_RDWR | O_SYNC);
129             if (fd < 0) {
130                 err = -1;
131             } else if (fstat(fd, &xst) || xst.st_ino != st.st_ino ||
132                        xst.st_dev != st.st_dev || xst.st_size != st.st_size) {
133                 fprintf(stderr, "%s: race condition on write\n", file);
134                 err = -2;
135             }
136             /* Write our own version ... */
137             if (xpwrite(fd, syslinux_adv, 2 * ADV_SIZE,
138                         st.st_size - 2 * ADV_SIZE) != 2 * ADV_SIZE) {
139                 err = -1;
140             }
141
142             sync();
143             set_attributes(fd);
144         }
145     }
146
147     if (err == -2)
148         fprintf(stderr, "%s: cannot write auxilliary data (need --update)?\n",
149                 file);
150     else if (err == -1)
151         perror(file);
152
153     if (fd >= 0)
154         close(fd);
155     if (file)
156         free(file);
157
158     return err;
159 }