Whamcloud - gitweb
create_inode: move debugfs internal state back to debugfs
[tools/e2fsprogs.git] / misc / create_inode.c
1 #include <time.h>
2 #include <unistd.h>
3
4 #include "create_inode.h"
5
6 #if __STDC_VERSION__ < 199901L
7 # if __GNUC__ >= 2
8 #  define __func__ __FUNCTION__
9 # else
10 #  define __func__ "<unknown>"
11 # endif
12 #endif
13
14 /* 64KiB is the minimium blksize to best minimize system call overhead. */
15 #ifndef IO_BUFSIZE
16 #define IO_BUFSIZE 64*1024
17 #endif
18
19 /* Block size for `st_blocks' */
20 #ifndef S_BLKSIZE
21 #define S_BLKSIZE 512
22 #endif
23
24 /* For saving the hard links */
25 int hdlink_cnt = HDLINK_CNT;
26
27 /* Link an inode number to a directory */
28 static errcode_t add_link(ext2_filsys fs, ext2_ino_t parent_ino,
29                           ext2_ino_t ino, const char *name)
30 {
31         struct ext2_inode       inode;
32         errcode_t               retval;
33
34         retval = ext2fs_read_inode(fs, ino, &inode);
35         if (retval) {
36                 com_err(__func__, retval, "while reading inode %u", ino);
37                 return retval;
38         }
39
40         retval = ext2fs_link(fs, parent_ino, name, ino, inode.i_flags);
41         if (retval == EXT2_ET_DIR_NO_SPACE) {
42                 retval = ext2fs_expand_dir(fs, parent_ino);
43                 if (retval) {
44                         com_err(__func__, retval, "while expanding directory");
45                         return retval;
46                 }
47                 retval = ext2fs_link(fs, parent_ino, name, ino, inode.i_flags);
48         }
49         if (retval) {
50                 com_err(__func__, retval, "while linking %s", name);
51                 return retval;
52         }
53
54         inode.i_links_count++;
55
56         retval = ext2fs_write_inode(fs, ino, &inode);
57         if (retval)
58                 com_err(__func__, retval, "while writing inode %u", ino);
59
60         return retval;
61 }
62
63 /* Fill the uid, gid, mode and time for the inode */
64 static void fill_inode(struct ext2_inode *inode, struct stat *st)
65 {
66         if (st != NULL) {
67                 inode->i_uid = st->st_uid;
68                 inode->i_gid = st->st_gid;
69                 inode->i_mode |= st->st_mode;
70                 inode->i_atime = st->st_atime;
71                 inode->i_mtime = st->st_mtime;
72                 inode->i_ctime = st->st_ctime;
73         }
74 }
75
76 /* Set the uid, gid, mode and time for the inode */
77 static errcode_t set_inode_extra(ext2_filsys fs, ext2_ino_t cwd,
78                                  ext2_ino_t ino, struct stat *st)
79 {
80         errcode_t               retval;
81         struct ext2_inode       inode;
82
83         retval = ext2fs_read_inode(fs, ino, &inode);
84         if (retval) {
85                 com_err(__func__, retval, "while reading inode %u", ino);
86                 return retval;
87         }
88
89         fill_inode(&inode, st);
90
91         retval = ext2fs_write_inode(fs, ino, &inode);
92         if (retval) {
93                 com_err(__func__, retval, "while writing inode %u", ino);
94                 return retval;
95         }
96 }
97
98 /* Make a special file which is block, character and fifo */
99 errcode_t do_mknod_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
100                             struct stat *st)
101 {
102         ext2_ino_t              ino;
103         errcode_t               retval;
104         struct ext2_inode       inode;
105         unsigned long           major, minor, mode;
106         int                     filetype;
107
108         switch(st->st_mode & S_IFMT) {
109         case S_IFCHR:
110                 mode = LINUX_S_IFCHR;
111                 filetype = EXT2_FT_CHRDEV;
112                 break;
113         case S_IFBLK:
114                 mode = LINUX_S_IFBLK;
115                 filetype =  EXT2_FT_BLKDEV;
116                 break;
117         case S_IFIFO:
118                 mode = LINUX_S_IFIFO;
119                 filetype = EXT2_FT_FIFO;
120                 break;
121         }
122
123         if (!(fs->flags & EXT2_FLAG_RW)) {
124                 com_err(__func__, 0, "Filesystem opened read/only");
125                 return -1;
126         }
127         retval = ext2fs_new_inode(fs, cwd, 010755, 0, &ino);
128         if (retval) {
129                 com_err(__func__, retval, 0);
130                 return retval;
131         }
132
133 #ifdef DEBUGFS
134         printf("Allocated inode: %u\n", ino);
135 #endif
136         retval = ext2fs_link(fs, cwd, name, ino, filetype);
137         if (retval == EXT2_ET_DIR_NO_SPACE) {
138                 retval = ext2fs_expand_dir(fs, cwd);
139                 if (retval) {
140                         com_err(__func__, retval, "while expanding directory");
141                         return retval;
142                 }
143                 retval = ext2fs_link(fs, cwd, name, ino, filetype);
144         }
145         if (retval) {
146                 com_err(name, retval, 0);
147                 return -1;
148         }
149         if (ext2fs_test_inode_bitmap2(fs->inode_map, ino))
150                 com_err(__func__, 0, "Warning: inode already set");
151         ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
152         memset(&inode, 0, sizeof(inode));
153         inode.i_mode = mode;
154         inode.i_atime = inode.i_ctime = inode.i_mtime =
155                 fs->now ? fs->now : time(0);
156
157         major = major(st->st_rdev);
158         minor = minor(st->st_rdev);
159
160         if ((major < 256) && (minor < 256)) {
161                 inode.i_block[0] = major * 256 + minor;
162                 inode.i_block[1] = 0;
163         } else {
164                 inode.i_block[0] = 0;
165                 inode.i_block[1] = (minor & 0xff) | (major << 8) |
166                                    ((minor & ~0xff) << 12);
167         }
168         inode.i_links_count = 1;
169
170         retval = ext2fs_write_new_inode(fs, ino, &inode);
171         if (retval)
172                 com_err(__func__, retval, "while creating inode %u", ino);
173
174         return retval;
175 }
176
177 /* Make a symlink name -> target */
178 errcode_t do_symlink_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
179                               char *target, ext2_ino_t root)
180 {
181         char                    *cp;
182         ext2_ino_t              parent_ino;
183         errcode_t               retval;
184         struct ext2_inode       inode;
185         struct stat             st;
186
187         cp = strrchr(name, '/');
188         if (cp) {
189                 *cp = 0;
190                 retval = ext2fs_namei(fs, root, cwd, name, &parent_ino);
191                 if (retval) {
192                         com_err(name, retval, 0);
193                         return retval;
194                 }
195                 name = cp+1;
196         } else
197                 parent_ino = cwd;
198
199 try_again:
200         retval = ext2fs_symlink(fs, parent_ino, 0, name, target);
201         if (retval == EXT2_ET_DIR_NO_SPACE) {
202                 retval = ext2fs_expand_dir(fs, parent_ino);
203                 if (retval) {
204                         com_err("do_symlink_internal", retval,
205                                 "while expanding directory");
206                         return retval;
207                 }
208                 goto try_again;
209         }
210         if (retval) {
211                 com_err("ext2fs_symlink", retval, 0);
212                 return retval;
213         }
214
215 }
216
217 /* Make a directory in the fs */
218 errcode_t do_mkdir_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
219                             struct stat *st, ext2_ino_t root)
220 {
221         char                    *cp;
222         ext2_ino_t              parent_ino, ino;
223         errcode_t               retval;
224         struct ext2_inode       inode;
225
226
227         cp = strrchr(name, '/');
228         if (cp) {
229                 *cp = 0;
230                 retval = ext2fs_namei(fs, root, cwd, name, &parent_ino);
231                 if (retval) {
232                         com_err(name, retval, 0);
233                         return retval;
234                 }
235                 name = cp+1;
236         } else
237                 parent_ino = cwd;
238
239 try_again:
240         retval = ext2fs_mkdir(fs, parent_ino, 0, name);
241         if (retval == EXT2_ET_DIR_NO_SPACE) {
242                 retval = ext2fs_expand_dir(fs, parent_ino);
243                 if (retval) {
244                         com_err(__func__, retval, "while expanding directory");
245                         return retval;
246                 }
247                 goto try_again;
248         }
249         if (retval) {
250                 com_err("ext2fs_mkdir", retval, 0);
251                 return retval;
252         }
253 }
254
255 static errcode_t copy_file(ext2_filsys fs, int fd, ext2_ino_t newfile,
256                            int bufsize, int make_holes)
257 {
258         ext2_file_t     e2_file;
259         errcode_t       retval;
260         int             got;
261         unsigned int    written;
262         char            *buf;
263         char            *ptr;
264         char            *zero_buf;
265         int             cmp;
266
267         retval = ext2fs_file_open(fs, newfile,
268                                   EXT2_FILE_WRITE, &e2_file);
269         if (retval)
270                 return retval;
271
272         retval = ext2fs_get_mem(bufsize, &buf);
273         if (retval) {
274                 com_err("copy_file", retval, "can't allocate buffer\n");
275                 return retval;
276         }
277
278         /* This is used for checking whether the whole block is zero */
279         retval = ext2fs_get_memzero(bufsize, &zero_buf);
280         if (retval) {
281                 com_err("copy_file", retval, "can't allocate buffer\n");
282                 ext2fs_free_mem(&buf);
283                 return retval;
284         }
285
286         while (1) {
287                 got = read(fd, buf, bufsize);
288                 if (got == 0)
289                         break;
290                 if (got < 0) {
291                         retval = errno;
292                         goto fail;
293                 }
294                 ptr = buf;
295
296                 /* Sparse copy */
297                 if (make_holes) {
298                         /* Check whether all is zero */
299                         cmp = memcmp(ptr, zero_buf, got);
300                         if (cmp == 0) {
301                                  /* The whole block is zero, make a hole */
302                                 retval = ext2fs_file_lseek(e2_file, got,
303                                                            EXT2_SEEK_CUR,
304                                                            NULL);
305                                 if (retval)
306                                         goto fail;
307                                 got = 0;
308                         }
309                 }
310
311                 /* Normal copy */
312                 while (got > 0) {
313                         retval = ext2fs_file_write(e2_file, ptr,
314                                                    got, &written);
315                         if (retval)
316                                 goto fail;
317
318                         got -= written;
319                         ptr += written;
320                 }
321         }
322         ext2fs_free_mem(&buf);
323         ext2fs_free_mem(&zero_buf);
324         retval = ext2fs_file_close(e2_file);
325         return retval;
326
327 fail:
328         ext2fs_free_mem(&buf);
329         ext2fs_free_mem(&zero_buf);
330         (void) ext2fs_file_close(e2_file);
331         return retval;
332 }
333
334 static int is_hardlink(ext2_ino_t ino)
335 {
336         int i;
337
338         for(i = 0; i < hdlinks.count; i++) {
339                 if(hdlinks.hdl[i].src_ino == ino)
340                         return i;
341         }
342         return -1;
343 }
344
345 /* Copy the native file to the fs */
346 errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src,
347                             const char *dest, ext2_ino_t root)
348 {
349         int             fd;
350         struct stat     statbuf;
351         ext2_ino_t      newfile;
352         errcode_t       retval;
353         struct ext2_inode inode;
354         int             bufsize = IO_BUFSIZE;
355         int             make_holes = 0;
356
357         fd = open(src, O_RDONLY);
358         if (fd < 0) {
359                 com_err(src, errno, 0);
360                 return errno;
361         }
362         if (fstat(fd, &statbuf) < 0) {
363                 com_err(src, errno, 0);
364                 close(fd);
365                 return errno;
366         }
367
368         retval = ext2fs_namei(fs, root, cwd, dest, &newfile);
369         if (retval == 0) {
370                 close(fd);
371                 return EXT2_ET_FILE_EXISTS;
372         }
373
374         retval = ext2fs_new_inode(fs, cwd, 010755, 0, &newfile);
375         if (retval) {
376                 com_err(__func__, retval, 0);
377                 close(fd);
378                 return retval;
379         }
380 #ifdef DEBUGFS
381         printf("Allocated inode: %u\n", newfile);
382 #endif
383         retval = ext2fs_link(fs, cwd, dest, newfile,
384                                 EXT2_FT_REG_FILE);
385         if (retval == EXT2_ET_DIR_NO_SPACE) {
386                 retval = ext2fs_expand_dir(fs, cwd);
387                 if (retval) {
388                         com_err(__func__, retval, "while expanding directory");
389                         close(fd);
390                         return retval;
391                 }
392                 retval = ext2fs_link(fs, cwd, dest, newfile,
393                                         EXT2_FT_REG_FILE);
394         }
395         if (retval) {
396                 com_err(dest, retval, 0);
397                 close(fd);
398                 return errno;
399         }
400         if (ext2fs_test_inode_bitmap2(fs->inode_map, newfile))
401                 com_err(__func__, 0, "Warning: inode already set");
402         ext2fs_inode_alloc_stats2(fs, newfile, +1, 0);
403         memset(&inode, 0, sizeof(inode));
404         inode.i_mode = (statbuf.st_mode & ~LINUX_S_IFMT) | LINUX_S_IFREG;
405         inode.i_atime = inode.i_ctime = inode.i_mtime =
406                 fs->now ? fs->now : time(0);
407         inode.i_links_count = 1;
408         inode.i_size = statbuf.st_size;
409         if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
410                                       EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
411                 inode.i_flags |= EXT4_INLINE_DATA_FL;
412         } else if (fs->super->s_feature_incompat &
413                    EXT3_FEATURE_INCOMPAT_EXTENTS) {
414                 int i;
415                 struct ext3_extent_header *eh;
416
417                 eh = (struct ext3_extent_header *) &inode.i_block[0];
418                 eh->eh_depth = 0;
419                 eh->eh_entries = 0;
420                 eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC);
421                 i = (sizeof(inode.i_block) - sizeof(*eh)) /
422                         sizeof(struct ext3_extent);
423                 eh->eh_max = ext2fs_cpu_to_le16(i);
424                 inode.i_flags |= EXT4_EXTENTS_FL;
425         }
426
427         retval = ext2fs_write_new_inode(fs, newfile, &inode);
428         if (retval) {
429                 com_err(__func__, retval, "while creating inode %u", newfile);
430                 close(fd);
431                 return retval;
432         }
433         if (inode.i_flags & EXT4_INLINE_DATA_FL) {
434                 retval = ext2fs_inline_data_init(fs, newfile);
435                 if (retval) {
436                         com_err("copy_file", retval, 0);
437                         close(fd);
438                         return retval;
439                 }
440         }
441         if (LINUX_S_ISREG(inode.i_mode)) {
442                 if (statbuf.st_blocks < statbuf.st_size / S_BLKSIZE) {
443                         make_holes = 1;
444                         /*
445                          * Use I/O blocksize as buffer size when
446                          * copying sparse files.
447                          */
448                         bufsize = statbuf.st_blksize;
449                 }
450                 retval = copy_file(fs, fd, newfile, bufsize, make_holes);
451                 if (retval)
452                         com_err("copy_file", retval, 0);
453         }
454         close(fd);
455
456         return retval;
457 }
458
459 /* Copy files from source_dir to fs */
460 errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
461                       const char *source_dir, ext2_ino_t root)
462 {
463         const char      *name;
464         DIR             *dh;
465         struct dirent   *dent;
466         struct stat     st;
467         char            ln_target[PATH_MAX];
468         unsigned int    save_inode;
469         ext2_ino_t      ino;
470         errcode_t       retval;
471         int             read_cnt;
472         int             hdlink;
473
474         if (chdir(source_dir) < 0) {
475                 com_err(__func__, errno,
476                         _("while changing working directory to \"%s\""),
477                         source_dir);
478                 return errno;
479         }
480
481         if (!(dh = opendir("."))) {
482                 com_err(__func__, errno,
483                         _("while opening directory \"%s\""), source_dir);
484                 return errno;
485         }
486
487         while ((dent = readdir(dh))) {
488                 if ((!strcmp(dent->d_name, ".")) ||
489                     (!strcmp(dent->d_name, "..")))
490                         continue;
491                 lstat(dent->d_name, &st);
492                 name = dent->d_name;
493
494                 /* Check for hardlinks */
495                 save_inode = 0;
496                 if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) &&
497                     st.st_nlink > 1) {
498                         hdlink = is_hardlink(st.st_ino);
499                         if (hdlink >= 0) {
500                                 retval = add_link(fs, parent_ino,
501                                                   hdlinks.hdl[hdlink].dst_ino,
502                                                   name);
503                                 if (retval) {
504                                         com_err(__func__, retval,
505                                                 "while linking %s", name);
506                                         return retval;
507                                 }
508                                 continue;
509                         } else
510                                 save_inode = 1;
511                 }
512
513                 switch(st.st_mode & S_IFMT) {
514                 case S_IFCHR:
515                 case S_IFBLK:
516                 case S_IFIFO:
517                         retval = do_mknod_internal(fs, parent_ino, name, &st);
518                         if (retval) {
519                                 com_err(__func__, retval,
520                                         _("while creating special file "
521                                           "\"%s\""), name);
522                                 return retval;
523                         }
524                         break;
525                 case S_IFSOCK:
526                         /* FIXME: there is no make socket function atm. */
527                         com_err(__func__, 0,
528                                 _("ignoring socket file \"%s\""), name);
529                         continue;
530                 case S_IFLNK:
531                         read_cnt = readlink(name, ln_target,
532                                             sizeof(ln_target));
533                         if (read_cnt == -1) {
534                                 com_err(__func__, errno,
535                                         _("while trying to readlink \"%s\""),
536                                         name);
537                                 return errno;
538                         }
539                         ln_target[read_cnt] = '\0';
540                         retval = do_symlink_internal(fs, parent_ino, name,
541                                                      ln_target, root);
542                         if (retval) {
543                                 com_err(__func__, retval,
544                                         _("while writing symlink\"%s\""),
545                                         name);
546                                 return retval;
547                         }
548                         break;
549                 case S_IFREG:
550                         retval = do_write_internal(fs, parent_ino, name, name,
551                                                    root);
552                         if (retval) {
553                                 com_err(__func__, retval,
554                                         _("while writing file \"%s\""), name);
555                                 return retval;
556                         }
557                         break;
558                 case S_IFDIR:
559                         retval = do_mkdir_internal(fs, parent_ino, name, &st,
560                                                    root);
561                         if (retval) {
562                                 com_err(__func__, retval,
563                                         _("while making dir \"%s\""), name);
564                                 return retval;
565                         }
566                         retval = ext2fs_namei(fs, root, parent_ino,
567                                               name, &ino);
568                         if (retval) {
569                                 com_err(name, retval, 0);
570                                         return retval;
571                         }
572                         /* Populate the dir recursively*/
573                         retval = populate_fs(fs, ino, name, root);
574                         if (retval) {
575                                 com_err(__func__, retval,
576                                         _("while adding dir \"%s\""), name);
577                                 return retval;
578                         }
579                         if (chdir("..")) {
580                                 com_err(__func__, errno,
581                                         _("during cd .."));
582                                 return errno;
583                         }
584                         break;
585                 default:
586                         com_err(__func__, 0,
587                                 _("ignoring entry \"%s\""), name);
588                 }
589
590                 retval =  ext2fs_namei(fs, root, parent_ino, name, &ino);
591                 if (retval) {
592                         com_err(name, retval, 0);
593                         return retval;
594                 }
595
596                 retval = set_inode_extra(fs, parent_ino, ino, &st);
597                 if (retval) {
598                         com_err(__func__, retval,
599                                 _("while setting inode for \"%s\""), name);
600                         return retval;
601                 }
602
603                 /* Save the hardlink ino */
604                 if (save_inode) {
605                         /*
606                          * Check whether need more memory, and we don't need
607                          * free() since the lifespan will be over after the fs
608                          * populated.
609                          */
610                         if (hdlinks.count == hdlink_cnt) {
611                                 if ((hdlinks.hdl = realloc (hdlinks.hdl,
612                                                 (hdlink_cnt + HDLINK_CNT) *
613                                                 sizeof (struct hdlink_s))) == NULL) {
614                                         com_err(name, errno, "Not enough memory");
615                                         return errno;
616                                 }
617                                 hdlink_cnt += HDLINK_CNT;
618                         }
619                         hdlinks.hdl[hdlinks.count].src_ino = st.st_ino;
620                         hdlinks.hdl[hdlinks.count].dst_ino = ino;
621                         hdlinks.count++;
622                 }
623         }
624         closedir(dh);
625         return retval;
626 }