Whamcloud - gitweb
libext2fs: create sockets when populating filesystem
[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 /* Link an inode number to a directory */
25 static errcode_t add_link(ext2_filsys fs, ext2_ino_t parent_ino,
26                           ext2_ino_t ino, const char *name)
27 {
28         struct ext2_inode       inode;
29         errcode_t               retval;
30
31         retval = ext2fs_read_inode(fs, ino, &inode);
32         if (retval) {
33                 com_err(__func__, retval, "while reading inode %u", ino);
34                 return retval;
35         }
36
37         retval = ext2fs_link(fs, parent_ino, name, ino, inode.i_flags);
38         if (retval == EXT2_ET_DIR_NO_SPACE) {
39                 retval = ext2fs_expand_dir(fs, parent_ino);
40                 if (retval) {
41                         com_err(__func__, retval, "while expanding directory");
42                         return retval;
43                 }
44                 retval = ext2fs_link(fs, parent_ino, name, ino, inode.i_flags);
45         }
46         if (retval) {
47                 com_err(__func__, retval, "while linking %s", name);
48                 return retval;
49         }
50
51         inode.i_links_count++;
52
53         retval = ext2fs_write_inode(fs, ino, &inode);
54         if (retval)
55                 com_err(__func__, retval, "while writing inode %u", ino);
56
57         return retval;
58 }
59
60 /* Fill the uid, gid, mode and time for the inode */
61 static void fill_inode(struct ext2_inode *inode, struct stat *st)
62 {
63         if (st != NULL) {
64                 inode->i_uid = st->st_uid;
65                 inode->i_gid = st->st_gid;
66                 inode->i_mode |= st->st_mode;
67                 inode->i_atime = st->st_atime;
68                 inode->i_mtime = st->st_mtime;
69                 inode->i_ctime = st->st_ctime;
70         }
71 }
72
73 /* Set the uid, gid, mode and time for the inode */
74 static errcode_t set_inode_extra(ext2_filsys fs, ext2_ino_t cwd,
75                                  ext2_ino_t ino, struct stat *st)
76 {
77         errcode_t               retval;
78         struct ext2_inode       inode;
79
80         retval = ext2fs_read_inode(fs, ino, &inode);
81         if (retval) {
82                 com_err(__func__, retval, "while reading inode %u", ino);
83                 return retval;
84         }
85
86         fill_inode(&inode, st);
87
88         retval = ext2fs_write_inode(fs, ino, &inode);
89         if (retval)
90                 com_err(__func__, retval, "while writing inode %u", ino);
91         return retval;
92 }
93
94 /* Make a special files (block and character devices), fifo's, and sockets  */
95 errcode_t do_mknod_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
96                             struct stat *st)
97 {
98         ext2_ino_t              ino;
99         errcode_t               retval;
100         struct ext2_inode       inode;
101         unsigned long           major, minor, mode;
102         int                     filetype;
103
104         switch(st->st_mode & S_IFMT) {
105         case S_IFCHR:
106                 mode = LINUX_S_IFCHR;
107                 filetype = EXT2_FT_CHRDEV;
108                 break;
109         case S_IFBLK:
110                 mode = LINUX_S_IFBLK;
111                 filetype =  EXT2_FT_BLKDEV;
112                 break;
113         case S_IFIFO:
114                 mode = LINUX_S_IFIFO;
115                 filetype = EXT2_FT_FIFO;
116                 break;
117         case S_IFSOCK:
118                 mode = LINUX_S_IFSOCK;
119                 filetype = EXT2_FT_SOCK;
120         default:
121                 abort();
122                 /* NOTREACHED */
123         }
124
125         if (!(fs->flags & EXT2_FLAG_RW)) {
126                 com_err(__func__, 0, "Filesystem opened read/only");
127                 return EROFS;
128         }
129         retval = ext2fs_new_inode(fs, cwd, 010755, 0, &ino);
130         if (retval) {
131                 com_err(__func__, retval, 0);
132                 return retval;
133         }
134
135 #ifdef DEBUGFS
136         printf("Allocated inode: %u\n", ino);
137 #endif
138         retval = ext2fs_link(fs, cwd, name, ino, filetype);
139         if (retval == EXT2_ET_DIR_NO_SPACE) {
140                 retval = ext2fs_expand_dir(fs, cwd);
141                 if (retval) {
142                         com_err(__func__, retval, "while expanding directory");
143                         return retval;
144                 }
145                 retval = ext2fs_link(fs, cwd, name, ino, filetype);
146         }
147         if (retval) {
148                 com_err(name, retval, 0);
149                 return -1;
150         }
151         if (ext2fs_test_inode_bitmap2(fs->inode_map, ino))
152                 com_err(__func__, 0, "Warning: inode already set");
153         ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
154         memset(&inode, 0, sizeof(inode));
155         inode.i_mode = mode;
156         inode.i_atime = inode.i_ctime = inode.i_mtime =
157                 fs->now ? fs->now : time(0);
158
159         major = major(st->st_rdev);
160         minor = minor(st->st_rdev);
161
162         if ((major < 256) && (minor < 256)) {
163                 inode.i_block[0] = major * 256 + minor;
164                 inode.i_block[1] = 0;
165         } else {
166                 inode.i_block[0] = 0;
167                 inode.i_block[1] = (minor & 0xff) | (major << 8) |
168                                    ((minor & ~0xff) << 12);
169         }
170         inode.i_links_count = 1;
171
172         retval = ext2fs_write_new_inode(fs, ino, &inode);
173         if (retval)
174                 com_err(__func__, retval, "while creating inode %u", ino);
175
176         return retval;
177 }
178
179 /* Make a symlink name -> target */
180 errcode_t do_symlink_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
181                               char *target, ext2_ino_t root)
182 {
183         char                    *cp;
184         ext2_ino_t              parent_ino;
185         errcode_t               retval;
186         struct ext2_inode       inode;
187         struct stat             st;
188
189         cp = strrchr(name, '/');
190         if (cp) {
191                 *cp = 0;
192                 retval = ext2fs_namei(fs, root, cwd, name, &parent_ino);
193                 if (retval) {
194                         com_err(name, retval, 0);
195                         return retval;
196                 }
197                 name = cp+1;
198         } else
199                 parent_ino = cwd;
200
201 try_again:
202         retval = ext2fs_symlink(fs, parent_ino, 0, name, target);
203         if (retval == EXT2_ET_DIR_NO_SPACE) {
204                 retval = ext2fs_expand_dir(fs, parent_ino);
205                 if (retval) {
206                         com_err("do_symlink_internal", retval,
207                                 "while expanding directory");
208                         return retval;
209                 }
210                 goto try_again;
211         }
212         if (retval)
213                 com_err("ext2fs_symlink", retval, 0);
214         return retval;
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 static errcode_t copy_file(ext2_filsys fs, int fd, ext2_ino_t newfile,
255                            int bufsize, int make_holes)
256 {
257         ext2_file_t     e2_file;
258         errcode_t       retval;
259         int             got;
260         unsigned int    written;
261         char            *buf;
262         char            *ptr;
263         char            *zero_buf;
264         int             cmp;
265
266         retval = ext2fs_file_open(fs, newfile,
267                                   EXT2_FILE_WRITE, &e2_file);
268         if (retval)
269                 return retval;
270
271         retval = ext2fs_get_mem(bufsize, &buf);
272         if (retval) {
273                 com_err("copy_file", retval, "can't allocate buffer\n");
274                 return retval;
275         }
276
277         /* This is used for checking whether the whole block is zero */
278         retval = ext2fs_get_memzero(bufsize, &zero_buf);
279         if (retval) {
280                 com_err("copy_file", retval, "can't allocate buffer\n");
281                 ext2fs_free_mem(&buf);
282                 return retval;
283         }
284
285         while (1) {
286                 got = read(fd, buf, bufsize);
287                 if (got == 0)
288                         break;
289                 if (got < 0) {
290                         retval = errno;
291                         goto fail;
292                 }
293                 ptr = buf;
294
295                 /* Sparse copy */
296                 if (make_holes) {
297                         /* Check whether all is zero */
298                         cmp = memcmp(ptr, zero_buf, got);
299                         if (cmp == 0) {
300                                  /* The whole block is zero, make a hole */
301                                 retval = ext2fs_file_lseek(e2_file, got,
302                                                            EXT2_SEEK_CUR,
303                                                            NULL);
304                                 if (retval)
305                                         goto fail;
306                                 got = 0;
307                         }
308                 }
309
310                 /* Normal copy */
311                 while (got > 0) {
312                         retval = ext2fs_file_write(e2_file, ptr,
313                                                    got, &written);
314                         if (retval)
315                                 goto fail;
316
317                         got -= written;
318                         ptr += written;
319                 }
320         }
321         ext2fs_free_mem(&buf);
322         ext2fs_free_mem(&zero_buf);
323         retval = ext2fs_file_close(e2_file);
324         return retval;
325
326 fail:
327         ext2fs_free_mem(&buf);
328         ext2fs_free_mem(&zero_buf);
329         (void) ext2fs_file_close(e2_file);
330         return retval;
331 }
332
333 static int is_hardlink(struct hdlinks_s *hdlinks, dev_t dev, ino_t ino)
334 {
335         int i;
336
337         for (i = 0; i < hdlinks->count; i++) {
338                 if (hdlinks->hdl[i].src_dev == dev &&
339                     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 static errcode_t __populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
461                                const char *source_dir, ext2_ino_t root,
462                                struct hdlinks_s *hdlinks)
463 {
464         const char      *name;
465         DIR             *dh;
466         struct dirent   *dent;
467         struct stat     st;
468         char            ln_target[PATH_MAX];
469         unsigned int    save_inode;
470         ext2_ino_t      ino;
471         errcode_t       retval = 0;
472         int             read_cnt;
473         int             hdlink;
474
475         if (chdir(source_dir) < 0) {
476                 com_err(__func__, errno,
477                         _("while changing working directory to \"%s\""),
478                         source_dir);
479                 return errno;
480         }
481
482         if (!(dh = opendir("."))) {
483                 com_err(__func__, errno,
484                         _("while opening directory \"%s\""), source_dir);
485                 return errno;
486         }
487
488         while ((dent = readdir(dh))) {
489                 if ((!strcmp(dent->d_name, ".")) ||
490                     (!strcmp(dent->d_name, "..")))
491                         continue;
492                 if (lstat(dent->d_name, &st)) {
493                         com_err(__func__, errno, _("while lstat \"%s\""),
494                                 dent->d_name);
495                         goto out;
496                 }
497                 name = dent->d_name;
498
499                 /* Check for hardlinks */
500                 save_inode = 0;
501                 if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) &&
502                     st.st_nlink > 1) {
503                         hdlink = is_hardlink(hdlinks, st.st_dev, st.st_ino);
504                         if (hdlink >= 0) {
505                                 retval = add_link(fs, parent_ino,
506                                                   hdlinks->hdl[hdlink].dst_ino,
507                                                   name);
508                                 if (retval) {
509                                         com_err(__func__, retval,
510                                                 "while linking %s", name);
511                                         goto out;
512                                 }
513                                 continue;
514                         } else
515                                 save_inode = 1;
516                 }
517
518                 switch(st.st_mode & S_IFMT) {
519                 case S_IFCHR:
520                 case S_IFBLK:
521                 case S_IFIFO:
522                 case S_IFSOCK:
523                         retval = do_mknod_internal(fs, parent_ino, name, &st);
524                         if (retval) {
525                                 com_err(__func__, retval,
526                                         _("while creating special file "
527                                           "\"%s\""), name);
528                                 goto out;
529                         }
530                         break;
531                 case S_IFLNK:
532                         read_cnt = readlink(name, ln_target,
533                                             sizeof(ln_target) - 1);
534                         if (read_cnt == -1) {
535                                 com_err(__func__, errno,
536                                         _("while trying to readlink \"%s\""),
537                                         name);
538                                 return errno;
539                         }
540                         ln_target[read_cnt] = '\0';
541                         retval = do_symlink_internal(fs, parent_ino, name,
542                                                      ln_target, root);
543                         if (retval) {
544                                 com_err(__func__, retval,
545                                         _("while writing symlink\"%s\""),
546                                         name);
547                                 goto out;
548                         }
549                         break;
550                 case S_IFREG:
551                         retval = do_write_internal(fs, parent_ino, name, name,
552                                                    root);
553                         if (retval) {
554                                 com_err(__func__, retval,
555                                         _("while writing file \"%s\""), name);
556                                 goto out;
557                         }
558                         break;
559                 case S_IFDIR:
560                         retval = do_mkdir_internal(fs, parent_ino, name, &st,
561                                                    root);
562                         if (retval) {
563                                 com_err(__func__, retval,
564                                         _("while making dir \"%s\""), name);
565                                 goto out;
566                         }
567                         retval = ext2fs_namei(fs, root, parent_ino,
568                                               name, &ino);
569                         if (retval) {
570                                 com_err(name, retval, 0);
571                                         goto out;
572                         }
573                         /* Populate the dir recursively*/
574                         retval = __populate_fs(fs, ino, name, root, hdlinks);
575                         if (retval) {
576                                 com_err(__func__, retval,
577                                         _("while adding dir \"%s\""), name);
578                                 goto out;
579                         }
580                         if (chdir("..")) {
581                                 com_err(__func__, errno, _("during cd .."));
582                                 retval = errno;
583                                 goto out;
584                         }
585                         break;
586                 default:
587                         com_err(__func__, 0,
588                                 _("ignoring entry \"%s\""), name);
589                 }
590
591                 retval =  ext2fs_namei(fs, root, parent_ino, name, &ino);
592                 if (retval) {
593                         com_err(name, retval, 0);
594                         goto out;
595                 }
596
597                 retval = set_inode_extra(fs, parent_ino, ino, &st);
598                 if (retval) {
599                         com_err(__func__, retval,
600                                 _("while setting inode for \"%s\""), name);
601                         goto out;
602                 }
603
604                 /* Save the hardlink ino */
605                 if (save_inode) {
606                         /*
607                          * Check whether need more memory, and we don't need
608                          * free() since the lifespan will be over after the fs
609                          * populated.
610                          */
611                         if (hdlinks->count == hdlinks->size) {
612                                 void *p = realloc(hdlinks->hdl,
613                                                 (hdlinks->size + HDLINK_CNT) *
614                                                 sizeof(struct hdlink_s));
615                                 if (p == NULL) {
616                                         com_err(name, errno,
617                                                 _("Not enough memory"));
618                                         retval = EXT2_ET_NO_MEMORY;
619                                         goto out;
620                                 }
621                                 hdlinks->hdl = p;
622                                 hdlinks->size += HDLINK_CNT;
623                         }
624                         hdlinks->hdl[hdlinks->count].src_dev = st.st_dev;
625                         hdlinks->hdl[hdlinks->count].src_ino = st.st_ino;
626                         hdlinks->hdl[hdlinks->count].dst_ino = ino;
627                         hdlinks->count++;
628                 }
629         }
630
631 out:
632         closedir(dh);
633         return retval;
634 }
635
636 errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
637                       const char *source_dir, ext2_ino_t root)
638 {
639         struct hdlinks_s hdlinks;
640         errcode_t retval;
641
642         hdlinks.count = 0;
643         hdlinks.size = HDLINK_CNT;
644         hdlinks.hdl = realloc(NULL, hdlinks.size * sizeof(struct hdlink_s));
645         if (hdlinks.hdl == NULL) {
646                 com_err(__func__, errno, "Not enough memory");
647                 return errno;
648         }
649
650         retval = __populate_fs(fs, parent_ino, source_dir, root, &hdlinks);
651
652         free(hdlinks.hdl);
653         return retval;
654 }