inline_data.c revision 31253488385a62e4db53cad04f55188a2861b4bb
1/*
2 * inline_data.c --- data in inode
3 *
4 * Copyright (C) 2012 Zheng Liu <wenqing.lz@taobao.com>
5 *
6 * %Begin-Header%
7 * This file may be redistributed under the terms of the GNU library
8 * General Public License, version 2.
9 * %End-Header%
10 */
11
12#include "config.h"
13#include <stdio.h>
14#include <time.h>
15
16#include "ext2_fs.h"
17#include "ext2_ext_attr.h"
18
19#include "ext2fs.h"
20#include "ext2fsP.h"
21
22struct ext2_inline_data {
23	ext2_filsys fs;
24	ext2_ino_t ino;
25	size_t ea_size;	/* the size of inline data in ea area */
26	void *ea_data;
27};
28
29static errcode_t ext2fs_inline_data_ea_set(struct ext2_inline_data *data)
30{
31	struct ext2_xattr_handle *handle;
32	errcode_t retval;
33
34	retval = ext2fs_xattrs_open(data->fs, data->ino, &handle);
35	if (retval)
36		return retval;
37
38	retval = ext2fs_xattrs_read(handle);
39	if (retval)
40		goto err;
41
42	retval = ext2fs_xattr_set(handle, "system.data",
43				  data->ea_data, data->ea_size);
44	if (retval)
45		goto err;
46
47	retval = ext2fs_xattrs_write(handle);
48
49err:
50	(void) ext2fs_xattrs_close(&handle);
51	return retval;
52}
53
54static errcode_t ext2fs_inline_data_ea_get(struct ext2_inline_data *data)
55{
56	struct ext2_xattr_handle *handle;
57	errcode_t retval;
58
59	data->ea_size = 0;
60	data->ea_data = 0;
61
62	retval = ext2fs_xattrs_open(data->fs, data->ino, &handle);
63	if (retval)
64		return retval;
65
66	retval = ext2fs_xattrs_read(handle);
67	if (retval)
68		goto err;
69
70	retval = ext2fs_xattr_get(handle, "system.data",
71				  (void **)&data->ea_data, &data->ea_size);
72	if (retval)
73		goto err;
74
75err:
76	(void) ext2fs_xattrs_close(&handle);
77	return retval;
78}
79
80errcode_t ext2fs_inline_data_init(ext2_filsys fs, ext2_ino_t ino)
81{
82	struct ext2_inline_data data;
83
84	data.fs = fs;
85	data.ino = ino;
86	data.ea_size = 0;
87	data.ea_data = "";
88	return ext2fs_inline_data_ea_set(&data);
89}
90
91errcode_t ext2fs_inline_data_size(ext2_filsys fs, ext2_ino_t ino, size_t *size)
92{
93	struct ext2_inode inode;
94	struct ext2_inline_data data;
95	errcode_t retval;
96
97	retval = ext2fs_read_inode(fs, ino, &inode);
98	if (retval)
99		return retval;
100
101	if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
102		return EXT2_ET_NO_INLINE_DATA;
103
104	data.fs = fs;
105	data.ino = ino;
106	retval = ext2fs_inline_data_ea_get(&data);
107	if (retval)
108		return retval;
109
110	*size = EXT4_MIN_INLINE_DATA_SIZE + data.ea_size;
111	return ext2fs_free_mem(&data.ea_data);
112}
113
114int ext2fs_inline_data_dir_iterate(ext2_filsys fs, ext2_ino_t ino,
115				   void *priv_data)
116{
117	struct dir_context *ctx;
118	struct ext2_inode inode;
119	struct ext2_dir_entry dirent;
120	struct ext2_inline_data data;
121	int ret = BLOCK_ABORT;
122	e2_blkcnt_t blockcnt = 0;
123
124	ctx = (struct dir_context *)priv_data;
125
126	ctx->errcode = ext2fs_read_inode(fs, ino, &inode);
127	if (ctx->errcode)
128		goto out;
129
130	if (!(inode.i_flags & EXT4_INLINE_DATA_FL)) {
131		ctx->errcode = EXT2_ET_NO_INLINE_DATA;
132		goto out;
133	}
134
135	if (!LINUX_S_ISDIR(inode.i_mode)) {
136		ctx->errcode = EXT2_ET_NO_DIRECTORY;
137		goto out;
138	}
139	ret = 0;
140
141	/* we first check '.' and '..' dir */
142	dirent.inode = ino;
143	dirent.name_len = 1;
144	ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(2), &dirent);
145	dirent.name[0] = '.';
146	dirent.name[1] = '\0';
147	ctx->buf = (char *)&dirent;
148	ext2fs_get_rec_len(fs, &dirent, &ctx->buflen);
149	ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
150	if (ret & BLOCK_ABORT)
151		goto out;
152
153	dirent.inode = ext2fs_le32_to_cpu(inode.i_block[0]);
154	dirent.name_len = 2;
155	ext2fs_set_rec_len(fs, EXT2_DIR_REC_LEN(3), &dirent);
156	dirent.name[0] = '.';
157	dirent.name[1] = '.';
158	dirent.name[2] = '\0';
159	ctx->buf = (char *)&dirent;
160	ext2fs_get_rec_len(fs, &dirent, &ctx->buflen);
161	ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
162	if (ret & BLOCK_INLINE_DATA_CHANGED) {
163		errcode_t err;
164
165		inode.i_block[0] = ext2fs_cpu_to_le32(dirent.inode);
166		err = ext2fs_write_inode(fs, ino, &inode);
167		if (err)
168			goto out;
169		ret &= ~BLOCK_INLINE_DATA_CHANGED;
170	}
171	if (ret & BLOCK_ABORT)
172		goto out;
173
174	ctx->buf = (char *)inode.i_block + EXT4_INLINE_DATA_DOTDOT_SIZE;
175	ctx->buflen = EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE;
176#ifdef WORDS_BIGENDIAN
177	ctx->errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
178	if (ctx->errcode) {
179		ret |= BLOCK_ABORT;
180		goto out;
181	}
182#endif
183	ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
184	if (ret & BLOCK_INLINE_DATA_CHANGED) {
185#ifdef WORDS_BIGENDIAN
186		ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
187						       ctx->buflen, 0);
188		if (ctx->errcode) {
189			ret |= BLOCK_ABORT;
190			goto out;
191		}
192#endif
193		ctx->errcode = ext2fs_write_inode(fs, ino, &inode);
194		if (ctx->errcode)
195			ret |= BLOCK_ABORT;
196		ret &= ~BLOCK_INLINE_DATA_CHANGED;
197	}
198	if (ret & BLOCK_ABORT)
199		goto out;
200
201	data.fs = fs;
202	data.ino = ino;
203	ctx->errcode = ext2fs_inline_data_ea_get(&data);
204	if (ctx->errcode) {
205		ret |= BLOCK_ABORT;
206		goto out;
207	}
208	if (data.ea_size <= 0)
209		goto out;
210
211	ctx->buf = data.ea_data;
212	ctx->buflen = data.ea_size;
213#ifdef WORDS_BIGENDIAN
214	ctx.errcode = ext2fs_dirent_swab_in2(fs, ctx->buf, ctx->buflen, 0);
215	if (ctx.errcode) {
216		ret |= BLOCK_ABORT;
217		goto out;
218	}
219#endif
220
221	ret |= ext2fs_process_dir_block(fs, 0, blockcnt++, 0, 0, priv_data);
222	if (ret & BLOCK_INLINE_DATA_CHANGED) {
223#ifdef WORDS_BIGENDIAN
224		ctx->errcode = ext2fs_dirent_swab_out2(fs, ctx->buf,
225						      ctx->buflen, 0);
226		if (ctx->errcode) {
227			ret |= BLOCK_ABORT;
228			goto out1;
229		}
230#endif
231		ctx->errcode = ext2fs_inline_data_ea_set(&data);
232		if (ctx->errcode)
233			ret |= BLOCK_ABORT;
234	}
235
236out1:
237	ext2fs_free_mem(&data.ea_data);
238	ctx->buf = 0;
239
240out:
241	ret &= ~(BLOCK_ABORT | BLOCK_INLINE_DATA_CHANGED);
242	return ret;
243}
244
245errcode_t ext2fs_inline_data_ea_remove(ext2_filsys fs, ext2_ino_t ino)
246{
247	struct ext2_xattr_handle *handle;
248	errcode_t retval;
249
250	retval = ext2fs_xattrs_open(fs, ino, &handle);
251	if (retval)
252		return retval;
253
254	retval = ext2fs_xattrs_read(handle);
255	if (retval)
256		goto err;
257
258	retval = ext2fs_xattr_remove(handle, "system.data");
259	if (retval)
260		goto err;
261
262	retval = ext2fs_xattrs_write(handle);
263
264err:
265	(void) ext2fs_xattrs_close(&handle);
266	return retval;
267}
268
269static errcode_t ext2fs_inline_data_convert_dir(ext2_filsys fs, ext2_ino_t ino,
270						char *bbuf, char *ibuf, int size)
271{
272	struct ext2_dir_entry *dir, *dir2;
273	struct ext2_dir_entry_tail *t;
274	errcode_t retval;
275	unsigned int offset;
276	int csum_size = 0;
277	int filetype = 0;
278	int rec_len;
279
280	if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
281				       EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
282		csum_size = sizeof(struct ext2_dir_entry_tail);
283
284	/* Create '.' and '..' */
285	if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
286				      EXT2_FEATURE_INCOMPAT_FILETYPE))
287		filetype = EXT2_FT_DIR;
288
289	/*
290	 * Set up entry for '.'
291	 */
292	dir = (struct ext2_dir_entry *) bbuf;
293	dir->inode = ino;
294	ext2fs_dirent_set_name_len(dir, 1);
295	ext2fs_dirent_set_file_type(dir, filetype);
296	dir->name[0] = '.';
297	rec_len = (fs->blocksize - csum_size) - EXT2_DIR_REC_LEN(1);
298	dir->rec_len = EXT2_DIR_REC_LEN(1);
299
300	/*
301	 * Set up entry for '..'
302	 */
303	dir = (struct ext2_dir_entry *) (bbuf + dir->rec_len);
304	dir->rec_len = EXT2_DIR_REC_LEN(2);
305	dir->inode = ext2fs_le32_to_cpu(((__u32 *)ibuf)[0]);
306	ext2fs_dirent_set_name_len(dir, 2);
307	ext2fs_dirent_set_file_type(dir, filetype);
308	dir->name[0] = '.';
309	dir->name[1] = '.';
310
311	/*
312	 * Ajust the last rec_len
313	 */
314	offset = EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2);
315	dir = (struct ext2_dir_entry *) (bbuf + offset);
316	memcpy(bbuf + offset, ibuf + EXT4_INLINE_DATA_DOTDOT_SIZE,
317	       size - EXT4_INLINE_DATA_DOTDOT_SIZE);
318	size += EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2) -
319		EXT4_INLINE_DATA_DOTDOT_SIZE;
320
321	do {
322		dir2 = dir;
323		retval = ext2fs_get_rec_len(fs, dir, &rec_len);
324		if (retval)
325			goto err;
326		offset += rec_len;
327		dir = (struct ext2_dir_entry *) (bbuf + offset);
328	} while (offset < size);
329	rec_len += fs->blocksize - csum_size - offset;
330	retval = ext2fs_set_rec_len(fs, rec_len, dir2);
331	if (retval)
332		goto err;
333
334	if (csum_size) {
335		t = EXT2_DIRENT_TAIL(bbuf, fs->blocksize);
336		ext2fs_initialize_dirent_tail(fs, t);
337	}
338
339err:
340	return retval;
341}
342
343static errcode_t
344ext2fs_inline_data_dir_expand(ext2_filsys fs, ext2_ino_t ino,
345			      struct ext2_inode *inode, char *buf, size_t size)
346{
347	errcode_t retval;
348	blk64_t blk;
349	char *blk_buf;
350
351	retval = ext2fs_get_memzero(fs->blocksize, &blk_buf);
352	if (retval)
353		return retval;
354
355#ifdef WORDS_BIGENDIAN
356	retval = ext2fs_dirent_swab_in2(fs, buf, size);
357	if (retval)
358		goto errout;
359#endif
360
361	/* Adjust the rec_len */
362	retval = ext2fs_inline_data_convert_dir(fs, ino, blk_buf, buf, size);
363	/* Allocate a new block */
364	retval = ext2fs_new_block2(fs, 0, 0, &blk);
365	if (retval)
366		goto errout;
367	retval = ext2fs_write_dir_block4(fs, blk, blk_buf, 0, ino);
368	if (retval)
369		goto errout;
370
371	/* Update inode */
372	if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT3_FEATURE_INCOMPAT_EXTENTS))
373		inode->i_flags |= EXT4_EXTENTS_FL;
374	inode->i_flags &= ~EXT4_INLINE_DATA_FL;
375	ext2fs_iblk_set(fs, inode, 1);
376	inode->i_size = fs->blocksize;
377	retval = ext2fs_bmap2(fs, ino, inode, 0, BMAP_SET, 0, 0, &blk);
378	if (retval)
379		goto errout;
380	retval = ext2fs_write_inode(fs, ino, inode);
381	if (retval)
382		goto errout;
383	ext2fs_block_alloc_stats(fs, blk, +1);
384
385errout:
386	ext2fs_free_mem(&blk_buf);
387	return retval;
388}
389
390static errcode_t
391ext2fs_inline_data_file_expand(ext2_filsys fs, ext2_ino_t ino,
392			       struct ext2_inode *inode, char *buf, size_t size)
393{
394	ext2_file_t e2_file;
395	errcode_t retval;
396
397	/* Update inode */
398	if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
399				      EXT3_FEATURE_INCOMPAT_EXTENTS)) {
400		int i;
401		struct ext3_extent_header *eh;
402
403		eh = (struct ext3_extent_header *) &inode->i_block[0];
404		eh->eh_depth = 0;
405		eh->eh_entries = 0;
406		eh->eh_magic = EXT3_EXT_MAGIC;
407		i = (sizeof(inode->i_block) - sizeof(*eh)) /
408			sizeof(struct ext3_extent);
409		eh->eh_max = ext2fs_cpu_to_le16(i);
410		inode->i_flags |= EXT4_EXTENTS_FL;
411	}
412	inode->i_flags &= ~EXT4_INLINE_DATA_FL;
413	ext2fs_iblk_set(fs, inode, 0);
414	inode->i_size = 0;
415	retval = ext2fs_write_inode(fs, ino, inode);
416	if (retval)
417		return retval;
418
419	/* Write out the block buffer */
420	retval = ext2fs_file_open(fs, ino, EXT2_FILE_WRITE, &e2_file);
421	if (retval)
422		return retval;
423	retval = ext2fs_file_write(e2_file, buf, size, 0);
424	ext2fs_file_close(e2_file);
425	return retval;
426}
427
428errcode_t ext2fs_inline_data_expand(ext2_filsys fs, ext2_ino_t ino)
429{
430	struct ext2_inode inode;
431	struct ext2_inline_data data;
432	errcode_t retval;
433	size_t inline_size;
434	char *inline_buf = 0;
435
436	EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
437
438	retval = ext2fs_read_inode(fs, ino, &inode);
439	if (retval)
440		return retval;
441
442	if (!(inode.i_flags & EXT4_INLINE_DATA_FL))
443		return EXT2_ET_NO_INLINE_DATA;
444
445	data.fs = fs;
446	data.ino = ino;
447	retval = ext2fs_inline_data_ea_get(&data);
448	if (retval)
449		return retval;
450	inline_size = data.ea_size + EXT4_MIN_INLINE_DATA_SIZE;
451	retval = ext2fs_get_mem(inline_size, &inline_buf);
452	if (retval)
453		goto errout;
454
455	memcpy(inline_buf, (void *)inode.i_block, EXT4_MIN_INLINE_DATA_SIZE);
456	if (data.ea_size > 0) {
457		memcpy(inline_buf + EXT4_MIN_INLINE_DATA_SIZE,
458		       data.ea_data, data.ea_size);
459	}
460
461	memset((void *)inode.i_block, 0, EXT4_MIN_INLINE_DATA_SIZE);
462	retval = ext2fs_inline_data_ea_remove(fs, ino);
463	if (retval)
464		goto errout;
465
466	if (LINUX_S_ISDIR(inode.i_mode)) {
467		retval = ext2fs_inline_data_dir_expand(fs, ino, &inode,
468						inline_buf, inline_size);
469	} else {
470		retval = ext2fs_inline_data_file_expand(fs, ino, &inode,
471						inline_buf, inline_size);
472	}
473
474errout:
475	if (inline_buf)
476		ext2fs_free_mem(&inline_buf);
477	ext2fs_free_mem(&data.ea_data);
478	return retval;
479}
480
481/*
482 * When caller uses this function to retrieve the inline data, it must
483 * allocate a buffer which has the size of inline data.  The size of
484 * inline data can be know by ext2fs_inline_data_get_size().
485 */
486errcode_t ext2fs_inline_data_get(ext2_filsys fs, ext2_ino_t ino,
487				 struct ext2_inode *inode,
488				 void *buf, size_t *size)
489{
490	struct ext2_inode inode_buf;
491	struct ext2_inline_data data;
492	errcode_t retval;
493
494	if (!inode) {
495		retval = ext2fs_read_inode(fs, ino, &inode_buf);
496		if (retval)
497			return retval;
498		inode = &inode_buf;
499	}
500
501	data.fs = fs;
502	data.ino = ino;
503	retval = ext2fs_inline_data_ea_get(&data);
504	if (retval)
505		return retval;
506
507	memcpy(buf, (void *)inode->i_block, EXT4_MIN_INLINE_DATA_SIZE);
508	if (data.ea_size > 0)
509		memcpy(buf + EXT4_MIN_INLINE_DATA_SIZE,
510		       data.ea_data, data.ea_size);
511
512	if (size)
513		*size = EXT4_MIN_INLINE_DATA_SIZE + data.ea_size;
514	ext2fs_free_mem(&data.ea_data);
515	return 0;
516}
517
518errcode_t ext2fs_inline_data_set(ext2_filsys fs, ext2_ino_t ino,
519				 struct ext2_inode *inode,
520				 void *buf, size_t size)
521{
522	struct ext2_inode inode_buf;
523	struct ext2_inline_data data;
524	errcode_t retval;
525	size_t max_size;
526
527	if (!inode) {
528		retval = ext2fs_read_inode(fs, ino, &inode_buf);
529		if (retval)
530			return retval;
531		inode = &inode_buf;
532	}
533
534	if (size <= EXT4_MIN_INLINE_DATA_SIZE) {
535		memcpy((void *)inode->i_block, buf, size);
536		return ext2fs_write_inode(fs, ino, inode);
537	}
538
539	retval = ext2fs_xattr_inode_max_size(fs, ino, &max_size);
540	if (retval)
541		return retval;
542
543	if (size - EXT4_MIN_INLINE_DATA_SIZE > max_size)
544		return EXT2_ET_INLINE_DATA_NO_SPACE;
545
546	memcpy((void *)inode->i_block, buf, EXT4_MIN_INLINE_DATA_SIZE);
547	retval = ext2fs_write_inode(fs, ino, inode);
548	if (retval)
549		return retval;
550	data.fs = fs;
551	data.ino = ino;
552	data.ea_size = size - EXT4_MIN_INLINE_DATA_SIZE;
553	data.ea_data = buf + EXT4_MIN_INLINE_DATA_SIZE;
554	return ext2fs_inline_data_ea_set(&data);
555}
556
557#ifdef DEBUG
558#include "e2p/e2p.h"
559
560/*
561 * The length of buffer is set to 64 because in inode's i_block member it only
562 * can save 60 bytes.  Thus this value can let the data being saved in extra
563 * space.
564 */
565#define BUFF_SIZE (64)
566
567static errcode_t file_test(ext2_filsys fs)
568{
569	struct ext2_inode inode;
570	ext2_ino_t newfile;
571	errcode_t retval;
572	size_t size;
573	char *buf = 0, *cmpbuf = 0;
574	int i;
575
576	/* create a new file */
577	retval = ext2fs_new_inode(fs, 2, 010755, 0, &newfile);
578	if (retval) {
579		com_err("file_test", retval, "while allocaing a new inode");
580		return 1;
581	}
582
583	memset(&inode, 0, sizeof(inode));
584	inode.i_flags |= EXT4_INLINE_DATA_FL;
585	inode.i_size = EXT4_MIN_INLINE_DATA_SIZE;
586	inode.i_mode = LINUX_S_IFREG;
587	retval = ext2fs_write_new_inode(fs, newfile, &inode);
588	if (retval) {
589		com_err("file_test", retval, "while writting a new inode");
590		return 1;
591	}
592
593	retval = ext2fs_inline_data_init(fs, newfile);
594	if (retval) {
595		com_err("file_test", retval, "while init 'system.data'");
596		return 1;
597	}
598
599	retval = ext2fs_inline_data_size(fs, newfile, &size);
600	if (retval) {
601		com_err("file_test", retval, "while getting size");
602		return 1;
603	}
604
605	if (size != EXT4_MIN_INLINE_DATA_SIZE) {
606		fprintf(stderr,
607			"tst_inline_data: size of inline data is wrong\n");
608		return 1;
609	}
610
611	ext2fs_get_mem(BUFF_SIZE, &buf);
612	memset(buf, 'a', BUFF_SIZE);
613	retval = ext2fs_inline_data_set(fs, newfile, 0, buf, BUFF_SIZE);
614	if (retval) {
615		com_err("file_test", retval,
616			"while setting inline data %s", buf);
617		goto err;
618	}
619
620	ext2fs_get_mem(BUFF_SIZE, &cmpbuf);
621	retval = ext2fs_inline_data_get(fs, newfile, 0, cmpbuf, &size);
622	if (retval) {
623		com_err("file_test", retval, "while getting inline data");
624		goto err;
625	}
626
627	if (size != BUFF_SIZE) {
628		fprintf(stderr,
629			"tst_inline_data: size %lu != buflen %lu\n",
630			size, BUFF_SIZE);
631		retval = 1;
632		goto err;
633	}
634
635	if (memcmp(buf, cmpbuf, BUFF_SIZE)) {
636		fprintf(stderr, "tst_inline_data: buf != cmpbuf\n");
637		retval = 1;
638		goto err;
639	}
640
641	retval = ext2fs_punch(fs, newfile, 0, 0, 0, ~0ULL);
642	if (retval) {
643		com_err("file_test", retval, "while truncating inode");
644		goto err;
645	}
646
647	/* reload inode and check isize */
648	ext2fs_read_inode(fs, newfile, &inode);
649	if (inode.i_size != 0) {
650		fprintf(stderr, "tst_inline_data: i_size should be 0\n");
651		retval = 1;
652	}
653
654err:
655	if (cmpbuf)
656		ext2fs_free_mem(&cmpbuf);
657	if (buf)
658		ext2fs_free_mem(&buf);
659	return retval;
660}
661
662static errcode_t dir_test(ext2_filsys fs)
663{
664	const char *dot_name = ".";
665	const char *stub_name = "stub";
666	const char *parent_name = "test";
667	ext2_ino_t parent, dir, tmp;
668	errcode_t retval;
669	char dirname[PATH_MAX];
670	int i;
671
672	retval = ext2fs_mkdir(fs, 11, 11, stub_name);
673	if (retval) {
674		com_err("dir_test", retval, "while creating %s dir", stub_name);
675		return retval;
676	}
677
678	retval = ext2fs_mkdir(fs, 11, 0, parent_name);
679	if (retval) {
680		com_err("dir_test", retval,
681			"while creating %s dir", parent_name);
682		return retval;
683	}
684
685	retval = ext2fs_lookup(fs, 11, parent_name, strlen(parent_name),
686			       0, &parent);
687	if (retval) {
688		com_err("dir_test", retval,
689			"while looking up %s dir", parent_name);
690		return retval;
691	}
692
693	retval = ext2fs_lookup(fs, parent, dot_name, strlen(dot_name),
694			       0, &tmp);
695	if (retval) {
696		com_err("dir_test", retval,
697			"while looking up %s dir", parent_name);
698		return retval;
699	}
700
701	if (parent != tmp) {
702		fprintf(stderr, "tst_inline_data: parent (%lu) != tmp (%lu)\n",
703			parent, tmp);
704		return 1;
705	}
706
707	for (i = 0, dir = 13; i < 4; i++, dir++) {
708		tmp = 0;
709		snprintf(dirname, PATH_MAX, "%d", i);
710		retval = ext2fs_mkdir(fs, parent, 0, dirname);
711		if (retval) {
712			com_err("dir_test", retval,
713				"while creating %s dir", dirname);
714			return retval;
715		}
716
717		retval = ext2fs_lookup(fs, parent, dirname, strlen(dirname),
718				       0, &tmp);
719		if (retval) {
720			com_err("dir_test", retval,
721				"while looking up %s dir", parent_name);
722			return retval;
723		}
724
725		if (dir != tmp) {
726			fprintf(stderr, "tst_inline_data: dir (%lu) != tmp (%lu)\n",
727				dir, tmp);
728			return 1;
729		}
730	}
731
732	snprintf(dirname, PATH_MAX, "%d", i);
733	retval = ext2fs_mkdir(fs, parent, 0, dirname);
734	if (retval && retval != EXT2_ET_DIR_NO_SPACE) {
735		com_err("dir_test", retval, "while creating %s dir", dirname);
736		return retval;
737	}
738
739	retval = ext2fs_expand_dir(fs, parent);
740	if (retval) {
741		com_err("dir_test", retval, "while expanding %s dir", parent_name);
742		return retval;
743	}
744
745	return 0;
746}
747
748int main(int argc, char *argv[])
749{
750	ext2_filsys		fs;
751	struct ext2_super_block param;
752	errcode_t		retval;
753	int			i;
754
755	/* setup */
756	initialize_ext2_error_table();
757
758	memset(&param, 0, sizeof(param));
759	ext2fs_blocks_count_set(&param, 32768);
760	param.s_inodes_count = 100;
761
762	param.s_feature_incompat |= EXT4_FEATURE_INCOMPAT_INLINE_DATA;
763	param.s_rev_level = EXT2_DYNAMIC_REV;
764	param.s_inode_size = 256;
765
766	retval = ext2fs_initialize("test fs", EXT2_FLAG_64BITS, &param,
767				   test_io_manager, &fs);
768	if (retval) {
769		com_err("setup", retval,
770			"while initializing filesystem");
771		exit(1);
772	}
773
774	retval = ext2fs_allocate_tables(fs);
775	if (retval) {
776		com_err("setup", retval,
777			"while allocating tables for test filesysmte");
778		exit(1);
779	}
780
781	/* initialize inode cache */
782	if (!fs->icache) {
783		struct ext2_inode inode;
784		ext2_ino_t first_ino = EXT2_FIRST_INO(fs->super);
785		int i;
786
787		/* we just want to init inode cache.  So ignore error */
788		ext2fs_create_inode_cache(fs, 16);
789		if (!fs->icache) {
790			fprintf(stderr,
791				"tst_inline_data: init inode cache failed\n");
792			exit(1);
793		}
794
795		/* setup inode cache */
796		for (i = 0; i < fs->icache->cache_size; i++)
797			fs->icache->cache[i].ino = first_ino++;
798	}
799
800	/* test */
801	if (file_test(fs)) {
802		fprintf(stderr, "tst_inline_data(FILE): FAILED\n");
803		return 1;
804	}
805	printf("tst_inline_data(FILE): OK\n");
806
807	if (dir_test(fs)) {
808		fprintf(stderr, "tst_inline_data(DIR): FAILED\n");
809		return 1;
810	}
811	printf("tst_inline_data(DIR): OK\n");
812
813	return 0;
814}
815#endif
816