Whamcloud - gitweb
e2scan: a tool for fast namespace/inode scanning
[tools/e2fsprogs.git] / e2scan / db.c
1 #define _GNU_SOURCE
2 #define _FILE_OFFSET_BITS 64
3
4 #include <stdio.h>
5 #include <time.h>
6 #include <unistd.h>
7 #include <ext2fs/ext2fs.h>
8 #include <sys/stat.h>
9
10 #if defined(HAVE_SQLITE3) && defined(HAVE_SQLITE3_H)
11
12 #include <sqlite3.h>
13
14 /* e2scan.c */
15 extern ext2_filsys fs;
16 extern struct {
17         int mode;
18         int nr;
19         union {
20                 struct {
21                         int fd;
22                         int nr_commands;
23                 } db;
24                 struct {
25                         /* number of files newer than specified time */
26                         ext2_ino_t nr_files;
27                         /* number of files reported */
28                         ext2_ino_t nr_reported;
29                         time_t mtimestamp;
30                         time_t ctimestamp;
31                 } fl;
32         };
33 } scan_data;
34
35 int block_iterate_cb(ext2_filsys fs, blk_t  *block_nr,
36                      e2_blkcnt_t blockcnt,
37                      blk_t ref_block EXT2FS_ATTR((unused)),
38                      int ref_offset EXT2FS_ATTR((unused)),
39                      void *priv_data);
40
41
42 static long count = 10000;
43
44 static void exec_one_sql_noreturn(sqlite3 *db, char *sqls)
45 {
46         char *errmsg = NULL;
47
48         if (sqlite3_exec(db, sqls, NULL, NULL, &errmsg) != SQLITE_OK) {
49                 fprintf(stderr, "SQL error: %s;\nrequest: %s", errmsg, sqls);
50                 sqlite3_free(errmsg);
51                 exit(1);
52         }
53 }
54
55 static void create_sql_table(sqlite3 *db, const char *table_name,
56                              const char *columns)
57 {
58         char *sqls;
59
60         if (asprintf(&sqls, "create table %s (%s)", table_name, columns) < 0) {
61                 perror("asprintf failed");
62                 exit(1);
63         }
64
65         exec_one_sql_noreturn(db, sqls);
66         free(sqls);
67 }
68
69 static void begin_one_transaction(sqlite3 *db)
70 {
71         exec_one_sql_noreturn(db, "BEGIN;");
72 }
73
74 static void commit_one_transaction(sqlite3 *db)
75 {
76         exec_one_sql_noreturn(db, "COMMIT;");
77 }
78
79 static void batch_one_transaction(sqlite3 *db)
80 {
81         static long num = 0;
82
83         num ++;
84         if (num % count == 0) {
85                 commit_one_transaction(db);
86                 begin_one_transaction(db);
87         }
88         /* else do nothing */
89 }
90
91 #define COLUMNS "ino, generation, parent, name, size, mtime, ctime, dtime"
92
93 static void create_full_db(const char *name, int fd)
94 {
95         sqlite3 *db;
96         int rd;
97         int nr;
98         char sqls[512];
99
100         if (sqlite3_open(name, &db) != SQLITE_OK) {
101                 fprintf(stderr, "failed to sqlite3_open: %s\n", name);
102                 sqlite3_close(db);
103                 exit(1);
104         }
105         create_sql_table(db, "dirs", COLUMNS);
106         create_sql_table(db, "files", COLUMNS);
107
108         begin_one_transaction(db);
109
110         nr = 0;
111         while (1) {
112                 rd = read(fd, sqls, 512);
113                 if (rd == -1) {
114                         perror("read failed\n");
115                         exit(1);
116                 }
117                 if (rd != 512)
118                         break;
119                 nr ++;
120                 exec_one_sql_noreturn(db, sqls);
121                 batch_one_transaction(db);
122         }
123         commit_one_transaction(db);
124         printf("database is created, %d records are inserted\n", nr);
125         sqlite3_close(db);
126 }
127
128 /* write sql command to pipe */
129 #define PIPECMDLEN 512
130 static void write_sql_command(int fd, const char *sqls)
131 {
132         char buf[PIPECMDLEN];
133
134         if (strlen(sqls) + 1 > PIPECMDLEN) {
135                 fprintf(stderr, "too long command");
136                 exit(1);
137         }
138         strcpy(buf, sqls);
139         if (write(fd, buf, PIPECMDLEN) != PIPECMDLEN) {
140                 perror("failed to write to pipe");
141                 exit(1);
142         }
143         scan_data.db.nr_commands ++;
144 }
145
146 pid_t fork_db_creation(const char *database)
147 {
148         int p[2];
149         struct stat st;
150         pid_t pid;
151
152         if (stat(database, &st) == 0) {
153                 fprintf(stderr, "%s exists. remove it first\n", database);
154                 exit(1);
155         }
156         if (pipe(p) == -1) {
157                 fprintf(stderr, "failed to pipe");
158                 exit(1);
159         }
160
161         pid = fork();
162         if (pid == (pid_t)-1) {
163                 fprintf(stderr, "failed to fork");
164                 exit(1);
165         }
166         if (pid == (pid_t)0) {
167                 /* child, read data written by parent and write to
168                  * database */
169                 close(p[1]);
170                 create_full_db(database, p[0]);
171                 close(p[0]);
172                 exit(0);
173         }
174
175         /* parent, read inodes and write them to pipe */
176         close(p[0]);
177         scan_data.db.fd = p[1];
178         return pid;
179 }
180
181 void database_iscan_action(ext2_ino_t ino, struct ext2_inode *inode,
182                            int fd, char *buf)
183 {
184         char *sqls;
185
186         if (LINUX_S_ISDIR(inode->i_mode)) {
187                 if (ino == EXT2_ROOT_INO) {
188                         sqls = sqlite3_mprintf("%s (%u,%u,%u,'%q',%u,%u,%u,%u)",
189                                                "insert into dirs values",
190                                                ino, inode->i_generation, ino, "/",
191                                                inode->i_size, inode->i_mtime,
192                                                inode->i_ctime, inode->i_dtime);
193                         write_sql_command(fd, sqls);
194                         sqlite3_free(sqls);
195                 }
196
197                 if (ext2fs_block_iterate2(fs, ino, 0, buf,
198                                           block_iterate_cb, &ino)) {
199                         fprintf(stderr, "ext2fs_block_iterate2 failed\n");
200                         exit(1);
201                 }
202         }
203 }
204
205 /*
206  * callback for ext2fs_dblist_dir_iterate to be called for each
207  * directory entry
208  */
209 int database_dblist_iterate_cb(ext2_ino_t dir, struct ext2_dir_entry *dirent,
210                                int namelen, int fd)
211 {
212         struct ext2_inode inode;
213         char *table;
214         char *sqls;
215         errcode_t retval;
216         char str[256];
217
218         if (!ext2fs_fast_test_inode_bitmap2(fs->inode_map, dirent->inode))
219                 /* entry of deleted file? can that ever happen */
220                 return 0;
221
222         retval = ext2fs_read_inode(fs, dirent->inode, &inode);
223         if (retval) {
224                 com_err("ext2fs_read_inode", retval, "while reading inode");
225                 exit(1);
226         }
227
228         if (LINUX_S_ISDIR(inode.i_mode))
229                 table = "dirs";
230         else
231                 table = "files";
232
233         sprintf(str, "%.*s", namelen, dirent->name);
234         sqls = sqlite3_mprintf("%s %s %s (%u,%u,%u,'%q',%u,%u,%u,%u)",
235                                "insert into", table, "values",
236                                dirent->inode, inode.i_generation, dir,
237                                str,
238                                inode.i_size, inode.i_mtime,
239                                inode.i_ctime, inode.i_dtime);
240         write_sql_command(fd, sqls);
241         sqlite3_free(sqls);
242
243         return 0;
244 }
245
246 #else
247
248 pid_t fork_db_creation(const char *database)
249 {
250         return 0;
251 }
252
253 void database_iscan_action(ext2_ino_t ino, struct ext2_inode *inode,
254                            int fd, char *buf)
255 {
256         return;
257 }
258
259 int database_dblist_iterate_cb(ext2_ino_t dir, struct ext2_dir_entry *dirent,
260                                int namelen, int fd)
261 {
262         return 0;
263 }
264
265 #endif