Line data Source code
1 : #include "clusterautoconfig.h"
2 :
3 : #include <stdio.h>
4 : #include <stdlib.h>
5 : #include <inttypes.h>
6 : #include <string.h>
7 : #include <dirent.h>
8 : #include <libintl.h>
9 : #define _(String) gettext(String)
10 :
11 : #include <logging.h>
12 : #include "libgfs2.h"
13 : #include "osi_list.h"
14 : #include "fsck.h"
15 : #include "lost_n_found.h"
16 : #include "link.h"
17 : #include "metawalk.h"
18 : #include "util.h"
19 : #include "afterpass1_common.h"
20 :
21 0 : static int attach_dotdot_to(struct fsck_cx *cx, uint64_t newdotdot,
22 : uint64_t olddotdot, uint64_t block)
23 : {
24 0 : const char *filename = "..";
25 0 : int filename_len = 2;
26 : int err;
27 : struct lgfs2_inode *ip, *pip;
28 : struct lgfs2_inum no;
29 :
30 0 : ip = fsck_load_inode(cx->sdp, block);
31 0 : pip = fsck_load_inode(cx->sdp, newdotdot);
32 : /* FIXME: Need to add some interactive
33 : * options here and come up with a
34 : * good default for non-interactive */
35 : /* FIXME: do i need to correct the
36 : * '..' entry for this directory in
37 : * this case? */
38 :
39 0 : if (lgfs2_dirent_del(ip, filename, filename_len))
40 0 : log_warn( _("Unable to remove \"..\" directory entry.\n"));
41 : else
42 0 : decr_link_count(cx, olddotdot, block, _("old \"..\""));
43 0 : no = pip->i_num;
44 0 : err = lgfs2_dir_add(ip, filename, filename_len, &no, DT_DIR);
45 0 : if (err) {
46 0 : log_err(_("Error adding directory %s: %s\n"),
47 : filename, strerror(errno));
48 0 : exit(FSCK_ERROR);
49 : }
50 0 : incr_link_count(cx, no, ip, _("new \"..\""));
51 0 : fsck_inode_put(&ip);
52 0 : fsck_inode_put(&pip);
53 0 : return 0;
54 : }
55 :
56 112 : static struct dir_info *mark_and_return_parent(struct fsck_cx *cx, struct dir_info *di)
57 : {
58 112 : struct lgfs2_sbd *sdp = cx->sdp;
59 : struct dir_info *pdi;
60 : int q_dotdot, q_treewalk;
61 112 : int error = 0;
62 : struct dir_info *dt_dotdot, *dt_treewalk;
63 :
64 112 : di->checked = 1;
65 :
66 112 : if (!di->treewalk_parent)
67 0 : return NULL;
68 :
69 112 : if (di->dotdot_parent.in_addr == di->treewalk_parent) {
70 112 : q_dotdot = bitmap_type(sdp, di->dotdot_parent.in_addr);
71 112 : if (q_dotdot != GFS2_BLKST_DINODE) {
72 0 : log_err(_("Orphaned directory at block %"PRIu64" (0x%"PRIx64") "
73 : "moved to lost+found\n"),
74 : di->dinode.in_addr, di->dinode.in_addr);
75 0 : return NULL;
76 : }
77 112 : goto out;
78 : }
79 :
80 0 : log_warn(_("Directory '..' and treewalk connections disagree for inode %"PRIu64" (0x%"PRIx64")\n"),
81 : di->dinode.in_addr, di->dinode.in_addr);
82 0 : log_notice(_("'..' has %"PRIu64" (0x%"PRIx64"), treewalk has %"PRIu64" (0x%"PRIx64")\n"),
83 : di->dotdot_parent.in_addr, di->dotdot_parent.in_addr, di->treewalk_parent, di->treewalk_parent);
84 0 : q_dotdot = bitmap_type(sdp, di->dotdot_parent.in_addr);
85 0 : dt_dotdot = dirtree_find(cx, di->dotdot_parent.in_addr);
86 0 : q_treewalk = bitmap_type(sdp, di->treewalk_parent);
87 0 : dt_treewalk = dirtree_find(cx, di->treewalk_parent);
88 : /* if the dotdot entry isn't a directory, but the
89 : * treewalk is, treewalk is correct - if the treewalk
90 : * entry isn't a directory, but the dotdot is, dotdot
91 : * is correct - if both are directories, which do we
92 : * choose? if neither are directories, we have a
93 : * problem - need to move this directory into lost+found
94 : */
95 0 : if (q_dotdot != GFS2_BLKST_DINODE || dt_dotdot == NULL) {
96 0 : if (q_treewalk != GFS2_BLKST_DINODE) {
97 0 : log_err( _("Orphaned directory, move to "
98 : "lost+found\n"));
99 0 : return NULL;
100 : } else {
101 0 : log_warn(_("Treewalk parent is correct, fixing dotdot -> %"PRIu64" (0x%"PRIx64")\n"),
102 : di->treewalk_parent, di->treewalk_parent);
103 0 : attach_dotdot_to(cx, di->treewalk_parent,
104 : di->dotdot_parent.in_addr,
105 : di->dinode.in_addr);
106 0 : di->dotdot_parent.in_addr = di->treewalk_parent;
107 : }
108 0 : goto out;
109 : }
110 0 : if (dt_treewalk) {
111 0 : log_err( _("Both .. and treewalk parents are directories, "
112 : "going with treewalk...\n"));
113 0 : attach_dotdot_to(cx, di->treewalk_parent,
114 : di->dotdot_parent.in_addr,
115 : di->dinode.in_addr);
116 0 : di->dotdot_parent.in_addr = di->treewalk_parent;
117 0 : goto out;
118 : }
119 0 : log_warn( _(".. parent is valid, but treewalk is bad - reattaching to "
120 : "lost+found"));
121 :
122 : /* FIXME: add a dinode for this entry instead? */
123 :
124 0 : if (!query(cx, _("Remove directory entry for bad inode %"PRIu64" (0x%"PRIx64") "
125 : "in %"PRIu64" (0x%"PRIx64")? (y/n)"),
126 : di->dinode.in_addr, di->dinode.in_addr,
127 : di->treewalk_parent, di->treewalk_parent)) {
128 0 : log_err( _("Directory entry to invalid inode remains\n"));
129 0 : return NULL;
130 : }
131 0 : error = remove_dentry_from_dir(cx, di->treewalk_parent, di->dinode.in_addr);
132 0 : if (error < 0) {
133 0 : stack;
134 0 : return NULL;
135 : }
136 0 : if (error > 0)
137 0 : log_warn(_("Unable to find dentry for block %"PRIu64" (0x%"PRIx64") "
138 : "in %"PRIu64" (0x%"PRIx64")\n"),
139 : di->dinode.in_addr, di->dinode.in_addr,
140 : di->treewalk_parent, di->treewalk_parent);
141 0 : log_warn( _("Directory entry removed\n"));
142 0 : log_info( _("Marking directory unlinked\n"));
143 :
144 0 : return NULL;
145 :
146 112 : out:
147 112 : pdi = dirtree_find(cx, di->dotdot_parent.in_addr);
148 :
149 112 : return pdi;
150 : }
151 :
152 : /**
153 : * pass3 - check connectivity of directories
154 : *
155 : * handle disconnected directories
156 : * handle lost+found directory errors (missing, not a directory, no space)
157 : */
158 56 : int pass3(struct fsck_cx *cx)
159 : {
160 56 : struct lgfs2_sbd *sdp = cx->sdp;
161 56 : struct osi_node *tmp, *next = NULL;
162 : struct dir_info *di, *tdi;
163 : struct lgfs2_inode *ip;
164 : int q;
165 :
166 56 : di = dirtree_find(cx, sdp->md.rooti->i_num.in_addr);
167 56 : if (di) {
168 56 : log_info( _("Marking root inode connected\n"));
169 56 : di->checked = 1;
170 : }
171 56 : di = dirtree_find(cx, sdp->master_dir->i_num.in_addr);
172 56 : if (di) {
173 56 : log_info(_("Marking master directory inode connected\n"));
174 56 : di->checked = 1;
175 : }
176 : /* Go through the directory list, working up through the parents
177 : * until we find one that's been checked already. If we don't
178 : * find a parent, put in lost+found.
179 : */
180 56 : log_info( _("Checking directory linkage.\n"));
181 280 : for (tmp = osi_first(&cx->dirtree); tmp; tmp = next) {
182 224 : next = osi_next(tmp);
183 224 : di = (struct dir_info *)tmp;
184 336 : while (!di->checked) {
185 : /* FIXME: Change this so it returns success or
186 : * failure and put the parent inode in a
187 : * param */
188 112 : if (skip_this_pass || fsck_abort) /* if asked to skip the rest */
189 0 : return FSCK_OK;
190 112 : tdi = mark_and_return_parent(cx, di);
191 :
192 112 : if (tdi) {
193 112 : log_debug(_("Directory at block %"PRIu64" (0x%"PRIx64") connected\n"),
194 : di->dinode.in_addr, di->dinode.in_addr);
195 112 : di = tdi;
196 112 : continue;
197 : }
198 0 : q = bitmap_type(sdp, di->dinode.in_addr);
199 0 : ip = fsck_load_inode(sdp, di->dinode.in_addr);
200 0 : if (q == GFS2_BLKST_FREE) {
201 0 : log_err( _("Found unlinked directory "
202 : "containing bad block at block %"PRIu64
203 : " (0x%"PRIx64")\n"),
204 : di->dinode.in_addr, di->dinode.in_addr);
205 0 : if (query(cx, _("Clear unlinked directory "
206 : "with bad blocks? (y/n) "))) {
207 0 : log_warn(_("inode %"PRIu64" (0x%"PRIx64") is "
208 : "now marked as free\n"),
209 : di->dinode.in_addr,
210 : di->dinode.in_addr);
211 0 : check_n_fix_bitmap(cx, ip->i_rgd,
212 : di->dinode.in_addr,
213 : 0, GFS2_BLKST_FREE);
214 0 : fsck_inode_put(&ip);
215 0 : break;
216 : } else
217 0 : log_err( _("Unlinked directory with bad block remains\n"));
218 : }
219 0 : if (q != GFS2_BLKST_DINODE) {
220 0 : log_err( _("Unlinked block marked as an inode "
221 : "is not an inode\n"));
222 0 : if (!query(cx, _("Clear the unlinked block? (y/n) "))) {
223 0 : log_err( _("The block was not "
224 : "cleared\n"));
225 0 : fsck_inode_put(&ip);
226 0 : break;
227 : }
228 0 : log_warn( _("inode %"PRIu64" (0x%"PRIx64") is now "
229 : "marked as free\n"),
230 : di->dinode.in_addr, di->dinode.in_addr);
231 0 : check_n_fix_bitmap(cx, ip->i_rgd,
232 : di->dinode.in_addr, 0,
233 : GFS2_BLKST_FREE);
234 0 : log_err( _("The block was cleared\n"));
235 0 : fsck_inode_put(&ip);
236 0 : break;
237 : }
238 :
239 0 : log_err(_("Found unlinked directory at block %"PRIu64" (0x%"PRIx64")\n"),
240 : di->dinode.in_addr, di->dinode.in_addr);
241 : /* Don't skip zero size directories with eattrs */
242 0 : if (!ip->i_size && !ip->i_eattr){
243 0 : log_err( _("Unlinked directory has zero "
244 : "size.\n"));
245 0 : if (query(cx, _("Remove zero-size unlinked "
246 : "directory? (y/n) "))) {
247 0 : fsck_bitmap_set(cx, ip, di->dinode.in_addr,
248 : _("zero-sized unlinked inode"),
249 : GFS2_BLKST_FREE);
250 0 : fsck_inode_put(&ip);
251 0 : break;
252 : } else {
253 0 : log_err( _("Zero-size unlinked "
254 : "directory remains\n"));
255 : }
256 : }
257 0 : if (query(cx, _("Add unlinked directory to "
258 : "lost+found? (y/n) "))) {
259 0 : if (add_inode_to_lf(cx, ip)) {
260 0 : fsck_inode_put(&ip);
261 0 : stack;
262 0 : return FSCK_ERROR;
263 : }
264 0 : log_warn( _("Directory relinked to lost+found\n"));
265 : } else {
266 0 : log_err( _("Unlinked directory remains unlinked\n"));
267 : }
268 0 : fsck_inode_put(&ip);
269 0 : break;
270 : }
271 : }
272 56 : if (lf_dip) {
273 0 : log_debug( _("At end of pass3, lost+found entries is %u\n"),
274 : lf_dip->i_entries);
275 : }
276 56 : return FSCK_OK;
277 : }
|