Whamcloud - gitweb
misc: coverity fixes
[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 file which is block, character and fifo */
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         default:
118                 abort();
119                 /* NOTREACHED */
120         }
121
122         if (!(fs->flags & EXT2_FLAG_RW)) {
123                 com_err(__func__, 0, "Filesystem opened read/only");
124                 return -1;
125         }
126         retval = ext2fs_new_inode(fs, cwd, 010755, 0, &ino);
127         if (retval) {
128                 com_err(__func__, retval, 0);
129                 return retval;
130         }
131
132 #ifdef DEBUGFS
133         printf("Allocated inode: %u\n", ino);
134 #endif
135         retval = ext2fs_link(fs, cwd, name, ino, filetype);
136         if (retval == EXT2_ET_DIR_NO_SPACE) {
137                 retval = ext2fs_expand_dir(fs, cwd);
138                 if (retval) {
139                         com_err(__func__, retval, "while expanding directory");
140                         return retval;
141                 }
142                 retval = ext2fs_link(fs, cwd, name, ino, filetype);
143         }
144         if (retval) {
145                 com_err(name, retval, 0);
146                 return -1;
147         }
148         if (ext2fs_test_inode_bitmap2(fs->inode_map, ino))
149                 com_err(__func__, 0, "Warning: inode already set");
150         ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
151         memset(&inode, 0, sizeof(inode));
152         inode.i_mode = mode;
153         inode.i_atime = inode.i_ctime = inode.i_mtime =
154                 fs->now ? fs->now : time(0);
155
156         major = major(st->st_rdev);
157         minor = minor(st->st_rdev);
158
159         if ((major < 256) && (minor < 256)) {
160                 inode.i_block[0] = major * 256 + minor;
161                 inode.i_block[1] = 0;
162         } else {
163                 inode.i_block[0] = 0;
164                 inode.i_block[1] = (minor & 0xff) | (major << 8) |
165                                    ((minor & ~0xff) << 12);
166         }
167         inode.i_links_count = 1;
168
169         retval = ext2fs_write_new_inode(fs, ino, &inode);
170         if (retval)
171                 com_err(__func__, retval, "while creating inode %u", ino);
172
173         return retval;
174 }
175
176 /* Make a symlink name -> target */
177 errcode_t do_symlink_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
178                               char *target, ext2_ino_t root)
179 {
180         char                    *cp;
181         ext2_ino_t              parent_ino;
182         errcode_t               retval;
183         struct ext2_inode       inode;
184         struct stat             st;
185
186         cp = strrchr(name, '/');
187         if (cp) {
188                 *cp = 0;
189                 retval = ext2fs_namei(fs, root, cwd, name, &parent_ino);
190                 if (retval) {
191                         com_err(name, retval, 0);
192                         return retval;
193                 }
194                 name = cp+1;
195         } else
196                 parent_ino = cwd;
197
198 try_again:
199         retval = ext2fs_symlink(fs, parent_ino, 0, name, target);
200         if (retval == EXT2_ET_DIR_NO_SPACE) {
201                 retval = ext2fs_expand_dir(fs, parent_ino);
202                 if (retval) {
203                         com_err("do_symlink_internal", retval,
204                                 "while expanding directory");
205                         return retval;
206                 }
207                 goto try_again;
208         }
209         if (retval)
210                 com_err("ext2fs_symlink", retval, 0);
211         return retval;
212 }
213
214 /* Make a directory in the fs */
215 errcode_t do_mkdir_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
216                             struct stat *st, ext2_ino_t root)
217 {
218         char                    *cp;
219         ext2_ino_t              parent_ino, ino;
220         errcode_t               retval;
221         struct ext2_inode       inode;
222
223
224         cp = strrchr(name, '/');
225         if (cp) {
226                 *cp = 0;
227                 retval = ext2fs_namei(fs, root, cwd, name, &parent_ino);
228                 if (retval) {
229                         com_err(name, retval, 0);
230                         return retval;
231                 }
232                 name = cp+1;
233         } else
234                 parent_ino = cwd;
235
236 try_again:
237         retval = ext2fs_mkdir(fs, parent_ino, 0, name);
238         if (retval == EXT2_ET_DIR_NO_SPACE) {
239                 retval = ext2fs_expand_dir(fs, parent_ino);
240                 if (retval) {
241                         com_err(__func__, retval, "while expanding directory");
242                         return retval;
243                 }
244                 goto try_again;
245         }
246         if (retval)
247                 com_err("ext2fs_mkdir", retval, 0);
248         return retval;
249 }
250
251 static errcode_t copy_file(ext2_filsys fs, int fd, ext2_ino_t newfile,
252                            int bufsize, int make_holes)
253 {
254         ext2_file_t     e2_file;
255         errcode_t       retval;
256         int             got;
257         unsigned int    written;
258         char            *buf;
259         char            *ptr;
260         char            *zero_buf;
261         int             cmp;
262
263         retval = ext2fs_file_open(fs, newfile,
264                                   EXT2_FILE_WRITE, &e2_file);
265         if (retval)
266                 return retval;
267
268         retval = ext2fs_get_mem(bufsize, &buf);
269         if (retval) {
270                 com_err("copy_file", retval, "can't allocate buffer\n");
271                 return retval;
272         }
273
274         /* This is used for checking whether the whole block is zero */
275         retval = ext2fs_get_memzero(bufsize, &zero_buf);
276         if (retval) {
277                 com_err("copy_file", retval, "can't allocate buffer\n");
278                 ext2fs_free_mem(&buf);
279                 return retval;
280         }
281
282         while (1) {
283                 got = read(fd, buf, bufsize);
284                 if (got == 0)
285                         break;
286                 if (got < 0) {
287                         retval = errno;
288                         goto fail;
289                 }
290                 ptr = buf;
291
292                 /* Sparse copy */
293                 if (make_holes) {
294                         /* Check whether all is zero */
295                         cmp = memcmp(ptr, zero_buf, got);
296                         if (cmp == 0) {
297                                  /* The whole block is zero, make a hole */
298                                 retval = ext2fs_file_lseek(e2_file, got,
299                                                            EXT2_SEEK_CUR,
300                                                            NULL);
301                                 if (retval)
302                                         goto fail;
303                                 got = 0;
304                         }
305                 }
306
307                 /* Normal copy */
308                 while (got > 0) {
309                         retval = ext2fs_file_write(e2_file, ptr,
310                                                    got, &written);
311                         if (retval)
312                                 goto fail;
313
314                         got -= written;
315                         ptr += written;
316                 }
317         }
318         ext2fs_free_mem(&buf);
319         ext2fs_free_mem(&zero_buf);
320         retval = ext2fs_file_close(e2_file);
321         return retval;
322
323 fail:
324         ext2fs_free_mem(&buf);
325         ext2fs_free_mem(&zero_buf);
326         (void) ext2fs_file_close(e2_file);
327         return retval;
328 }
329
330 static int is_hardlink(struct hdlinks_s *hdlinks, dev_t dev, ino_t ino)
331 {
332         int i;
333
334         for (i = 0; i < hdlinks->count; i++) {
335                 if (hdlinks->hdl[i].src_dev == dev &&
336                     hdlinks->hdl[i].src_ino == ino)
337                         return i;
338         }
339         return -1;
340 }
341
342 /* Copy the native file to the fs */
343 errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src,
344                             const char *dest, ext2_ino_t root)
345 {
346         int             fd;
347         struct stat     statbuf;
348         ext2_ino_t      newfile;
349         errcode_t       retval;
350         struct ext2_inode inode;
351         int             bufsize = IO_BUFSIZE;
352         int             make_holes = 0;
353
354         fd = open(src, O_RDONLY);
355         if (fd < 0) {
356                 com_err(src, errno, 0);
357                 return errno;
358         }
359         if (fstat(fd, &statbuf) < 0) {
360                 com_err(src, errno, 0);
361                 close(fd);
362                 return errno;
363         }
364
365         retval = ext2fs_namei(fs, root, cwd, dest, &newfile);
366         if (retval == 0) {
367                 close(fd);
368                 return EXT2_ET_FILE_EXISTS;
369         }
370
371         retval = ext2fs_new_inode(fs, cwd, 010755, 0, &newfile);
372         if (retval) {
373                 com_err(__func__, retval, 0);
374                 close(fd);
375                 return retval;
376         }
377 #ifdef DEBUGFS
378         printf("Allocated inode: %u\n", newfile);
379 #endif
380         retval = ext2fs_link(fs, cwd, dest, newfile,
381                                 EXT2_FT_REG_FILE);
382         if (retval == EXT2_ET_DIR_NO_SPACE) {
383                 retval = ext2fs_expand_dir(fs, cwd);
384                 if (retval) {
385                         com_err(__func__, retval, "while expanding directory");
386                         close(fd);
387                         return retval;
388                 }
389                 retval = ext2fs_link(fs, cwd, dest, newfile,
390                                         EXT2_FT_REG_FILE);
391         }
392         if (retval) {
393                 com_err(dest, retval, 0);
394                 close(fd);
395                 return errno;
396         }
397         if (ext2fs_test_inode_bitmap2(fs->inode_map, newfile))
398                 com_err(__func__, 0, "Warning: inode already set");
399         ext2fs_inode_alloc_stats2(fs, newfile, +1, 0);
400         memset(&inode, 0, sizeof(inode));
401         inode.i_mode = (statbuf.st_mode & ~LINUX_S_IFMT) | LINUX_S_IFREG;
402         inode.i_atime = inode.i_ctime = inode.i_mtime =
403                 fs->now ? fs->now : time(0);
404         inode.i_links_count = 1;
405         inode.i_size = statbuf.st_size;
406         if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
407                                       EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
408                 inode.i_flags |= EXT4_INLINE_DATA_FL;
409         } else if (fs->super->s_feature_incompat &
410                    EXT3_FEATURE_INCOMPAT_EXTENTS) {
411                 int i;
412                 struct ext3_extent_header *eh;
413
414                 eh = (struct ext3_extent_header *) &inode.i_block[0];
415                 eh->eh_depth = 0;
416                 eh->eh_entries = 0;
417                 eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC);
418                 i = (sizeof(inode.i_block) - sizeof(*eh)) /
419                         sizeof(struct ext3_extent);
420                 eh->eh_max = ext2fs_cpu_to_le16(i);
421                 inode.i_flags |= EXT4_EXTENTS_FL;
422         }
423
424         retval = ext2fs_write_new_inode(fs, newfile, &inode);
425         if (retval) {
426                 com_err(__func__, retval, "while creating inode %u", newfile);
427                 close(fd);
428                 return retval;
429         }
430         if (inode.i_flags & EXT4_INLINE_DATA_FL) {
431                 retval = ext2fs_inline_data_init(fs, newfile);
432                 if (retval) {
433                         com_err("copy_file", retval, 0);
434                         close(fd);
435                         return retval;
436                 }
437         }
438         if (LINUX_S_ISREG(inode.i_mode)) {
439                 if (statbuf.st_blocks < statbuf.st_size / S_BLKSIZE) {
440                         make_holes = 1;
441                         /*
442                          * Use I/O blocksize as buffer size when
443                          * copying sparse files.
444                          */
445                         bufsize = statbuf.st_blksize;
446                 }
447                 retval = copy_file(fs, fd, newfile, bufsize, make_holes);
448                 if (retval)
449                         com_err("copy_file", retval, 0);
450         }
451         close(fd);
452
453         return retval;
454 }
455
456 /* Copy files from source_dir to fs */
457 static errcode_t __populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
458                                const char *source_dir, ext2_ino_t root,
459                                struct hdlinks_s *hdlinks)
460 {
461         const char      *name;
462         DIR             *dh;
463         struct dirent   *dent;
464         struct stat     st;
465         char            ln_target[PATH_MAX];
466         unsigned int    save_inode;
467         ext2_ino_t      ino;
468         errcode_t       retval = 0;
469         int             read_cnt;
470         int             hdlink;
471
472         if (chdir(source_dir) < 0) {
473                 com_err(__func__, errno,
474                         _("while changing working directory to \"%s\""),
475                         source_dir);
476                 return errno;
477         }
478
479         if (!(dh = opendir("."))) {
480                 com_err(__func__, errno,
481                         _("while opening directory \"%s\""), source_dir);
482                 return errno;
483         }
484
485         while ((dent = readdir(dh))) {
486                 if ((!strcmp(dent->d_name, ".")) ||
487                     (!strcmp(dent->d_name, "..")))
488                         continue;
489                 if (lstat(dent->d_name, &st)) {
490                         com_err(__func__, errno, _("while lstat \"%s\""),
491                                 dent->d_name);
492                         goto out;
493                 }
494                 name = dent->d_name;
495
496                 /* Check for hardlinks */
497                 save_inode = 0;
498                 if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) &&
499                     st.st_nlink > 1) {
500                         hdlink = is_hardlink(hdlinks, st.st_dev, st.st_ino);
501                         if (hdlink >= 0) {
502                                 retval = add_link(fs, parent_ino,
503                                                   hdlinks->hdl[hdlink].dst_ino,
504                                                   name);
505                                 if (retval) {
506                                         com_err(__func__, retval,
507                                                 "while linking %s", name);
508                                         goto out;
509                                 }
510                                 continue;
511                         } else
512                                 save_inode = 1;
513                 }
514
515                 switch(st.st_mode & S_IFMT) {
516                 case S_IFCHR:
517                 case S_IFBLK:
518                 case S_IFIFO:
519                         retval = do_mknod_internal(fs, parent_ino, name, &st);
520                         if (retval) {
521                                 com_err(__func__, retval,
522                                         _("while creating special file "
523                                           "\"%s\""), name);
524                                 goto out;
525                         }
526                         break;
527                 case S_IFSOCK:
528                         /* FIXME: there is no make socket function atm. */
529                         com_err(__func__, 0,
530                                 _("ignoring socket file \"%s\""), name);
531                         continue;
532                 case S_IFLNK:
533                         read_cnt = readlink(name, ln_target,
534                                             sizeof(ln_target) - 1);
535                         if (read_cnt == -1) {
536                                 com_err(__func__, errno,
537                                         _("while trying to readlink \"%s\""),
538                                         name);
539                                 return errno;
540                         }
541                         ln_target[read_cnt] = '\0';
542                         retval = do_symlink_internal(fs, parent_ino, name,
543                                                      ln_target, root);
544                         if (retval) {
545                                 com_err(__func__, retval,
546                                         _("while writing symlink\"%s\""),
547                                         name);
548                                 goto out;
549                         }
550                         break;
551                 case S_IFREG:
552                         retval = do_write_internal(fs, parent_ino, name, name,
553                                                    root);
554                         if (retval) {
555                                 com_err(__func__, retval,
556                                         _("while writing file \"%s\""), name);
557                                 goto out;
558                         }
559                         break;
560                 case S_IFDIR:
561                         retval = do_mkdir_internal(fs, parent_ino, name, &st,
562                                                    root);
563                         if (retval) {
564                                 com_err(__func__, retval,
565                                         _("while making dir \"%s\""), name);
566                                 goto out;
567                         }
568                         retval = ext2fs_namei(fs, root, parent_ino,
569                                               name, &ino);
570                         if (retval) {
571                                 com_err(name, retval, 0);
572                                         goto out;
573                         }
574                         /* Populate the dir recursively*/
575                         retval = __populate_fs(fs, ino, name, root, hdlinks);
576                         if (retval) {
577                                 com_err(__func__, retval,
578                                         _("while adding dir \"%s\""), name);
579                                 goto out;
580                         }
581                         if (chdir("..")) {
582                                 com_err(__func__, errno, _("during cd .."));
583                                 retval = errno;
584                                 goto out;
585                         }
586                         break;
587                 default:
588                         com_err(__func__, 0,
589                                 _("ignoring entry \"%s\""), name);
590                 }
591
592                 retval =  ext2fs_namei(fs, root, parent_ino, name, &ino);
593                 if (retval) {
594                         com_err(name, retval, 0);
595                         goto out;
596                 }
597
598                 retval = set_inode_extra(fs, parent_ino, ino, &st);
599                 if (retval) {
600                         com_err(__func__, retval,
601                                 _("while setting inode for \"%s\""), name);
602                         goto out;
603                 }
604
605                 /* Save the hardlink ino */
606                 if (save_inode) {
607                         /*
608                          * Check whether need more memory, and we don't need
609                          * free() since the lifespan will be over after the fs
610                          * populated.
611                          */
612                         if (hdlinks->count == hdlinks->size) {
613                                 void *p = realloc(hdlinks->hdl,
614                                                 (hdlinks->size + HDLINK_CNT) *
615                                                 sizeof(struct hdlink_s));
616                                 if (p == NULL) {
617                                         com_err(name, errno,
618                                                 _("Not enough memory"));
619                                         retval = EXT2_ET_NO_MEMORY;
620                                         goto out;
621                                 }
622                                 hdlinks->hdl = p;
623                                 hdlinks->size += HDLINK_CNT;
624                         }
625                         hdlinks->hdl[hdlinks->count].src_dev = st.st_dev;
626                         hdlinks->hdl[hdlinks->count].src_ino = st.st_ino;
627                         hdlinks->hdl[hdlinks->count].dst_ino = ino;
628                         hdlinks->count++;
629                 }
630         }
631
632 out:
633         closedir(dh);
634         return retval;
635 }
636
637 errcode_t populate_fs(ext2_filsys fs, ext2_ino_t parent_ino,
638                       const char *source_dir, ext2_ino_t root)
639 {
640         struct hdlinks_s hdlinks;
641         errcode_t retval;
642
643         hdlinks.count = 0;
644         hdlinks.size = HDLINK_CNT;
645         hdlinks.hdl = realloc(NULL, hdlinks.size * sizeof(struct hdlink_s));
646         if (hdlinks.hdl == NULL) {
647                 com_err(__func__, errno, "Not enough memory");
648                 return errno;
649         }
650
651         retval = __populate_fs(fs, parent_ino, source_dir, root, &hdlinks);
652
653         free(hdlinks.hdl);
654         return retval;
655 }