1/*
2 * Copyright (c) 2012-2013 Paulo Alcantara <pcacjr@zytor.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write the Free Software Foundation,
15 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
16 */
17
18#include <cache.h>
19#include <core.h>
20#include <fs.h>
21
22#include "xfs_types.h"
23#include "xfs_sb.h"
24#include "xfs_ag.h"
25#include "misc.h"
26#include "xfs.h"
27#include "xfs_dinode.h"
28#include "xfs_dir2.h"
29
30#include "xfs_readdir.h"
31
32static int fill_dirent(struct fs_info *fs, struct dirent *dirent,
33		       uint32_t offset, xfs_ino_t ino, char *name,
34		       size_t namelen)
35{
36    xfs_dinode_t *core;
37
38    xfs_debug("fs %p, dirent %p offset %lu ino %llu name %s namelen %llu", fs,
39	      dirent, offset, ino, name, namelen);
40
41    dirent->d_ino = ino;
42    dirent->d_off = offset;
43    dirent->d_reclen = offsetof(struct dirent, d_name) + namelen + 1;
44
45    core = xfs_dinode_get_core(fs, ino);
46    if (!core) {
47        xfs_error("Failed to get dinode from disk (ino 0x%llx)", ino);
48        return -1;
49    }
50
51    if (be16_to_cpu(core->di_mode) & S_IFDIR)
52	dirent->d_type = DT_DIR;
53    else if (be16_to_cpu(core->di_mode) & S_IFREG)
54	dirent->d_type = DT_REG;
55    else if (be16_to_cpu(core->di_mode) & S_IFLNK)
56        dirent->d_type = DT_LNK;
57
58    memcpy(dirent->d_name, name, namelen);
59    dirent->d_name[namelen] = '\0';
60
61    return 0;
62}
63
64int xfs_readdir_dir2_local(struct file *file, struct dirent *dirent,
65			   xfs_dinode_t *core)
66{
67    xfs_dir2_sf_t *sf = (xfs_dir2_sf_t *)&core->di_literal_area[0];
68    xfs_dir2_sf_entry_t *sf_entry;
69    uint8_t count = sf->hdr.i8count ? sf->hdr.i8count : sf->hdr.count;
70    uint32_t offset = file->offset;
71    uint8_t *start_name;
72    uint8_t *end_name;
73    xfs_ino_t ino;
74    struct fs_info *fs = file->fs;
75    int retval = 0;
76
77    xfs_debug("file %p dirent %p core %p", file, dirent, core);
78    xfs_debug("count %hhu i8count %hhu", sf->hdr.count, sf->hdr.i8count);
79
80    if (file->offset + 1 > count)
81	goto out;
82
83    file->offset++;
84
85    sf_entry = (xfs_dir2_sf_entry_t *)((uint8_t *)&sf->list[0] -
86				       (!sf->hdr.i8count ? 4 : 0));
87
88    if (file->offset - 1) {
89	offset = file->offset;
90	while (--offset) {
91	    sf_entry = (xfs_dir2_sf_entry_t *)(
92				(uint8_t *)sf_entry +
93				offsetof(struct xfs_dir2_sf_entry,
94					 name[0]) +
95				sf_entry->namelen +
96				(sf->hdr.i8count ? 8 : 4));
97	}
98    }
99
100    start_name = &sf_entry->name[0];
101    end_name = start_name + sf_entry->namelen;
102
103    ino = xfs_dir2_sf_get_inumber(sf, (xfs_dir2_inou_t *)(
104				      (uint8_t *)sf_entry +
105				      offsetof(struct xfs_dir2_sf_entry,
106					       name[0]) +
107				      sf_entry->namelen));
108
109    retval = fill_dirent(fs, dirent, file->offset, ino, (char *)start_name,
110			 end_name - start_name);
111    if (retval)
112	xfs_error("Failed to fill in dirent structure");
113
114    return retval;
115
116out:
117    xfs_dir2_dirblks_flush_cache();
118
119    return -1;
120}
121
122int xfs_readdir_dir2_block(struct file *file, struct dirent *dirent,
123			   xfs_dinode_t *core)
124{
125    xfs_bmbt_irec_t r;
126    block_t dir_blk;
127    struct fs_info *fs = file->fs;
128    const uint8_t *dirblk_buf;
129    uint8_t *p;
130    uint32_t offset;
131    xfs_dir2_data_hdr_t *hdr;
132    xfs_dir2_block_tail_t *btp;
133    xfs_dir2_data_unused_t *dup;
134    xfs_dir2_data_entry_t *dep;
135    uint8_t *start_name;
136    uint8_t *end_name;
137    xfs_ino_t ino;
138    int retval = 0;
139
140    xfs_debug("file %p dirent %p core %p", file, dirent, core);
141
142    bmbt_irec_get(&r, (xfs_bmbt_rec_t *)&core->di_literal_area[0]);
143    dir_blk = fsblock_to_bytes(fs, r.br_startblock) >> BLOCK_SHIFT(fs);
144
145    dirblk_buf = xfs_dir2_dirblks_get_cached(fs, dir_blk, r.br_blockcount);
146    hdr = (xfs_dir2_data_hdr_t *)dirblk_buf;
147    if (be32_to_cpu(hdr->magic) != XFS_DIR2_BLOCK_MAGIC) {
148        xfs_error("Block directory header's magic number does not match!");
149        xfs_debug("hdr->magic: 0x%lx", be32_to_cpu(hdr->magic));
150	goto out;
151    }
152
153    btp = xfs_dir2_block_tail_p(XFS_INFO(fs), hdr);
154
155    if (file->offset + 1 > be32_to_cpu(btp->count))
156	goto out;
157
158    file->offset++;
159
160    p = (uint8_t *)(hdr + 1);
161
162    if (file->offset - 1) {
163	offset = file->offset;
164	while (--offset) {
165	    dep = (xfs_dir2_data_entry_t *)p;
166
167	    dup = (xfs_dir2_data_unused_t *)p;
168	    if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
169		p += be16_to_cpu(dup->length);
170		continue;
171	    }
172
173	    p += xfs_dir2_data_entsize(dep->namelen);
174	}
175    }
176
177    dep = (xfs_dir2_data_entry_t *)p;
178
179    start_name = &dep->name[0];
180    end_name = start_name + dep->namelen;
181
182    ino = be64_to_cpu(dep->inumber);
183
184    retval = fill_dirent(fs, dirent, file->offset, ino, (char *)start_name,
185			 end_name - start_name);
186    if (retval)
187	xfs_error("Failed to fill in dirent structure");
188
189    return retval;
190
191out:
192    xfs_dir2_dirblks_flush_cache();
193
194    return -1;
195}
196
197int xfs_readdir_dir2_leaf(struct file *file, struct dirent *dirent,
198			  xfs_dinode_t *core)
199{
200    xfs_bmbt_irec_t irec;
201    struct fs_info *fs = file->fs;
202    xfs_dir2_leaf_t *leaf;
203    block_t leaf_blk, dir_blk;
204    xfs_dir2_leaf_entry_t *lep;
205    uint32_t db;
206    unsigned int offset;
207    xfs_dir2_data_entry_t *dep;
208    xfs_dir2_data_hdr_t *data_hdr;
209    uint8_t *start_name;
210    uint8_t *end_name;
211    xfs_intino_t ino;
212    const uint8_t *buf = NULL;
213    int retval = 0;
214
215    xfs_debug("file %p dirent %p core %p", file, dirent, core);
216
217    bmbt_irec_get(&irec, ((xfs_bmbt_rec_t *)&core->di_literal_area[0]) +
218					be32_to_cpu(core->di_nextents) - 1);
219    leaf_blk = fsblock_to_bytes(fs, irec.br_startblock) >>
220					BLOCK_SHIFT(file->fs);
221
222    leaf = (xfs_dir2_leaf_t *)xfs_dir2_dirblks_get_cached(fs, leaf_blk,
223							  irec.br_blockcount);
224    if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAF1_MAGIC) {
225        xfs_error("Single leaf block header's magic number does not match!");
226        goto out;
227    }
228
229    if (!leaf->hdr.count)
230        goto out;
231
232    if (file->offset + 1 > be16_to_cpu(leaf->hdr.count))
233	goto out;
234
235    lep = &leaf->ents[file->offset++];
236
237    /* Skip over stale leaf entries */
238    for ( ; be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR;
239	  lep++, file->offset++);
240
241    db = xfs_dir2_dataptr_to_db(fs, be32_to_cpu(lep->address));
242
243    bmbt_irec_get(&irec, (xfs_bmbt_rec_t *)&core->di_literal_area[0] + db);
244
245    dir_blk = fsblock_to_bytes(fs, irec.br_startblock) >> BLOCK_SHIFT(fs);
246
247    buf = xfs_dir2_dirblks_get_cached(fs, dir_blk, irec.br_blockcount);
248    data_hdr = (xfs_dir2_data_hdr_t *)buf;
249    if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) {
250	xfs_error("Leaf directory's data magic number does not match!");
251	goto out;
252    }
253
254    offset = xfs_dir2_dataptr_to_off(fs, be32_to_cpu(lep->address));
255
256    dep = (xfs_dir2_data_entry_t *)((uint8_t *)buf + offset);
257
258    start_name = &dep->name[0];
259    end_name = start_name + dep->namelen;
260
261    ino = be64_to_cpu(dep->inumber);
262
263    retval = fill_dirent(fs, dirent, file->offset, ino, (char *)start_name,
264			 end_name - start_name);
265    if (retval)
266	xfs_error("Failed to fill in dirent structure");
267
268    return retval;
269
270out:
271    xfs_dir2_dirblks_flush_cache();
272
273    return -1;
274}
275
276int xfs_readdir_dir2_node(struct file *file, struct dirent *dirent,
277			  xfs_dinode_t *core)
278{
279    struct fs_info *fs = file->fs;
280    xfs_bmbt_irec_t irec;
281    uint32_t node_off = 0;
282    block_t fsblkno;
283    xfs_da_intnode_t *node = NULL;
284    struct inode *inode = file->inode;
285    int error;
286    xfs_dir2_data_hdr_t *data_hdr;
287    xfs_dir2_leaf_t *leaf;
288    xfs_dir2_leaf_entry_t *lep;
289    unsigned int offset;
290    xfs_dir2_data_entry_t *dep;
291    uint8_t *start_name;
292    uint8_t *end_name;
293    uint32_t db;
294    const uint8_t *buf = NULL;
295    int retval = 0;
296
297    xfs_debug("file %p dirent %p core %p", file, dirent, core);
298
299    do {
300        bmbt_irec_get(&irec, (xfs_bmbt_rec_t *)&core->di_literal_area[0] +
301								++node_off);
302    } while (irec.br_startoff < xfs_dir2_byte_to_db(fs, XFS_DIR2_LEAF_OFFSET));
303
304    fsblkno = fsblock_to_bytes(fs, irec.br_startblock) >> BLOCK_SHIFT(fs);
305
306    node = (xfs_da_intnode_t *)xfs_dir2_dirblks_get_cached(fs, fsblkno, 1);
307    if (be16_to_cpu(node->hdr.info.magic) != XFS_DA_NODE_MAGIC) {
308        xfs_error("Node's magic number does not match!");
309        goto out;
310    }
311
312try_next_btree:
313    if (!node->hdr.count ||
314	XFS_PVT(inode)->i_btree_offset >= be16_to_cpu(node->hdr.count))
315	goto out;
316
317    fsblkno = be32_to_cpu(node->btree[XFS_PVT(inode)->i_btree_offset].before);
318    fsblkno = xfs_dir2_get_right_blk(fs, core, fsblkno, &error);
319    if (error) {
320        xfs_error("Cannot find leaf rec!");
321        goto out;
322    }
323
324    leaf = (xfs_dir2_leaf_t*)xfs_dir2_dirblks_get_cached(fs, fsblkno, 1);
325    if (be16_to_cpu(leaf->hdr.info.magic) != XFS_DIR2_LEAFN_MAGIC) {
326        xfs_error("Leaf's magic number does not match!");
327        goto out;
328    }
329
330    if (!leaf->hdr.count ||
331	XFS_PVT(inode)->i_leaf_ent_offset >= be16_to_cpu(leaf->hdr.count)) {
332	XFS_PVT(inode)->i_btree_offset++;
333	XFS_PVT(inode)->i_leaf_ent_offset = 0;
334	goto try_next_btree;
335    }
336
337    lep = &leaf->ents[XFS_PVT(inode)->i_leaf_ent_offset];
338
339    /* Skip over stale leaf entries */
340    for ( ; XFS_PVT(inode)->i_leaf_ent_offset < be16_to_cpu(leaf->hdr.count) &&
341			be32_to_cpu(lep->address) == XFS_DIR2_NULL_DATAPTR;
342	  lep++, XFS_PVT(inode)->i_leaf_ent_offset++);
343
344    if (XFS_PVT(inode)->i_leaf_ent_offset == be16_to_cpu(leaf->hdr.count)) {
345	XFS_PVT(inode)->i_btree_offset++;
346	XFS_PVT(inode)->i_leaf_ent_offset = 0;
347	goto try_next_btree;
348    } else {
349	XFS_PVT(inode)->i_leaf_ent_offset++;
350    }
351
352    db = xfs_dir2_dataptr_to_db(fs, be32_to_cpu(lep->address));
353
354    fsblkno = xfs_dir2_get_right_blk(fs, core, db, &error);
355    if (error) {
356	xfs_error("Cannot find data block!");
357	goto out;
358    }
359
360    buf = xfs_dir2_dirblks_get_cached(fs, fsblkno, 1);
361    data_hdr = (xfs_dir2_data_hdr_t *)buf;
362    if (be32_to_cpu(data_hdr->magic) != XFS_DIR2_DATA_MAGIC) {
363	xfs_error("Leaf directory's data magic No. does not match!");
364	goto out;
365    }
366
367    offset = xfs_dir2_dataptr_to_off(fs, be32_to_cpu(lep->address));
368
369    dep = (xfs_dir2_data_entry_t *)((uint8_t *)buf + offset);
370
371    start_name = &dep->name[0];
372    end_name = start_name + dep->namelen;
373
374    retval = fill_dirent(fs, dirent, 0, be64_to_cpu(dep->inumber),
375			 (char *)start_name, end_name - start_name);
376    if (retval)
377	xfs_error("Failed to fill in dirent structure");
378
379    return retval;
380
381out:
382    xfs_dir2_dirblks_flush_cache();
383
384    XFS_PVT(inode)->i_btree_offset = 0;
385    XFS_PVT(inode)->i_leaf_ent_offset = 0;
386
387    return -1;
388}
389