tzh
2024-08-22 c7d0944258c7d0943aa7b2211498fd612971ce27
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/*
 * bmove.c --- Move blocks around to make way for a particular
 *     filesystem structure.
 *
 * Copyright (C) 1997 Theodore Ts'o.
 *
 * %Begin-Header%
 * This file may be redistributed under the terms of the GNU Library
 * General Public License, version 2.
 * %End-Header%
 */
 
#include "config.h"
#include <stdio.h>
#include <string.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
 
#include "ext2_fs.h"
#include "ext2fsP.h"
 
struct process_block_struct {
   ext2_ino_t        ino;
   struct ext2_inode *    inode;
   ext2fs_block_bitmap    reserve;
   ext2fs_block_bitmap    alloc_map;
   errcode_t        error;
   char            *buf;
   int            add_dir;
   int            flags;
};
 
static int process_block(ext2_filsys fs, blk64_t *block_nr,
            e2_blkcnt_t blockcnt, blk64_t ref_block,
            int ref_offset, void *priv_data)
{
   struct process_block_struct *pb;
   errcode_t    retval;
   int        ret;
   blk64_t        block, orig;
 
   pb = (struct process_block_struct *) priv_data;
   block = orig = *block_nr;
   ret = 0;
 
   /*
    * Let's see if this is one which we need to relocate
    */
   if (ext2fs_test_block_bitmap2(pb->reserve, block)) {
       do {
           if (++block >= ext2fs_blocks_count(fs->super))
               block = fs->super->s_first_data_block;
           if (block == orig) {
               pb->error = EXT2_ET_BLOCK_ALLOC_FAIL;
               return BLOCK_ABORT;
           }
       } while (ext2fs_test_block_bitmap2(pb->reserve, block) ||
            ext2fs_test_block_bitmap2(pb->alloc_map, block));
 
       retval = io_channel_read_blk64(fs->io, orig, 1, pb->buf);
       if (retval) {
           pb->error = retval;
           return BLOCK_ABORT;
       }
       retval = io_channel_write_blk64(fs->io, block, 1, pb->buf);
       if (retval) {
           pb->error = retval;
           return BLOCK_ABORT;
       }
       *block_nr = block;
       ext2fs_mark_block_bitmap2(pb->alloc_map, block);
       ret = BLOCK_CHANGED;
       if (pb->flags & EXT2_BMOVE_DEBUG)
           printf("ino=%u, blockcnt=%lld, %llu->%llu\n",
                  (unsigned) pb->ino, blockcnt, 
                  (unsigned long long) orig,
                  (unsigned long long) block);
   }
   if (pb->add_dir) {
       retval = ext2fs_add_dir_block2(fs->dblist, pb->ino,
                          block, blockcnt);
       if (retval) {
           pb->error = retval;
           ret |= BLOCK_ABORT;
       }
   }
   return ret;
}
 
errcode_t ext2fs_move_blocks(ext2_filsys fs,
                ext2fs_block_bitmap reserve,
                ext2fs_block_bitmap alloc_map,
                int flags)
{
   ext2_ino_t    ino;
   struct ext2_inode inode;
   errcode_t    retval;
   struct process_block_struct pb;
   ext2_inode_scan    scan;
   char        *block_buf;
 
   retval = ext2fs_open_inode_scan(fs, 0, &scan);
   if (retval)
       return retval;
 
   pb.reserve = reserve;
   pb.error = 0;
   pb.alloc_map = alloc_map ? alloc_map : fs->block_map;
   pb.flags = flags;
 
   retval = ext2fs_get_array(4, fs->blocksize, &block_buf);
   if (retval)
       return retval;
   pb.buf = block_buf + fs->blocksize * 3;
 
   /*
    * If GET_DBLIST is set in the flags field, then we should
    * gather directory block information while we're doing the
    * block move.
    */
   if (flags & EXT2_BMOVE_GET_DBLIST) {
       if (fs->dblist) {
           ext2fs_free_dblist(fs->dblist);
           fs->dblist = NULL;
       }
       retval = ext2fs_init_dblist(fs, 0);
       if (retval)
           return retval;
   }
 
   retval = ext2fs_get_next_inode(scan, &ino, &inode);
   if (retval)
       return retval;
 
   while (ino) {
       if ((inode.i_links_count == 0) ||
           !ext2fs_inode_has_valid_blocks2(fs, &inode))
           goto next;
 
       pb.ino = ino;
       pb.inode = &inode;
 
       pb.add_dir = (LINUX_S_ISDIR(inode.i_mode) &&
                 flags & EXT2_BMOVE_GET_DBLIST);
 
       retval = ext2fs_block_iterate3(fs, ino, 0, block_buf,
                          process_block, &pb);
       if (retval)
           return retval;
       if (pb.error)
           return pb.error;
 
   next:
       retval = ext2fs_get_next_inode(scan, &ino, &inode);
       if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE)
           goto next;
   }
   return 0;
}