Whamcloud - gitweb
create_inode: whitespace 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 /* 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_ino_t parent_ino, ext2_ino_t ino,
29                           const char *name)
30 {
31         struct ext2_inode       inode;
32         errcode_t               retval;
33
34         retval = ext2fs_read_inode(current_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(current_fs, parent_ino, name, ino, inode.i_flags);
41         if (retval == EXT2_ET_DIR_NO_SPACE) {
42                 retval = ext2fs_expand_dir(current_fs, parent_ino);
43                 if (retval) {
44                         com_err(__func__, retval, "while expanding directory");
45                         return retval;
46                 }
47                 retval = ext2fs_link(current_fs, parent_ino, name, ino,
48                                      inode.i_flags);
49         }
50         if (retval) {
51                 com_err(__func__, retval, "while linking %s", name);
52                 return retval;
53         }
54
55         inode.i_links_count++;
56
57         retval = ext2fs_write_inode(current_fs, ino, &inode);
58         if (retval)
59                 com_err(__func__, retval, "while writing inode %u", ino);
60
61         return retval;
62 }
63
64 /* Fill the uid, gid, mode and time for the inode */
65 static void fill_inode(struct ext2_inode *inode, struct stat *st)
66 {
67         if (st != NULL) {
68                 inode->i_uid = st->st_uid;
69                 inode->i_gid = st->st_gid;
70                 inode->i_mode |= st->st_mode;
71                 inode->i_atime = st->st_atime;
72                 inode->i_mtime = st->st_mtime;
73                 inode->i_ctime = st->st_ctime;
74         }
75 }
76
77 /* Set the uid, gid, mode and time for the inode */
78 errcode_t set_inode_extra(ext2_ino_t cwd, ext2_ino_t ino, struct stat *st)
79 {
80         errcode_t               retval;
81         struct ext2_inode       inode;
82
83         retval = ext2fs_read_inode(current_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(current_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_ino_t cwd, const char *name, struct stat *st)
100 {
101         ext2_ino_t              ino;
102         errcode_t               retval;
103         struct ext2_inode       inode;
104         unsigned long           major, minor, mode;
105         int                     filetype;
106
107         switch(st->st_mode & S_IFMT) {
108         case S_IFCHR:
109                 mode = LINUX_S_IFCHR;
110                 filetype = EXT2_FT_CHRDEV;
111                 break;
112         case S_IFBLK:
113                 mode = LINUX_S_IFBLK;
114                 filetype =  EXT2_FT_BLKDEV;
115                 break;
116         case S_IFIFO:
117                 mode = LINUX_S_IFIFO;
118                 filetype = EXT2_FT_FIFO;
119                 break;
120         }
121
122         if (!(current_fs->flags & EXT2_FLAG_RW)) {
123                 com_err(__func__, 0, "Filesystem opened read/only");
124                 return -1;
125         }
126         retval = ext2fs_new_inode(current_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(current_fs, cwd, name, ino, filetype);
136         if (retval == EXT2_ET_DIR_NO_SPACE) {
137                 retval = ext2fs_expand_dir(current_fs, cwd);
138                 if (retval) {
139                         com_err(__func__, retval, "while expanding directory");
140                         return retval;
141                 }
142                 retval = ext2fs_link(current_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(current_fs->inode_map, ino))
149                 com_err(__func__, 0, "Warning: inode already set");
150         ext2fs_inode_alloc_stats2(current_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                 current_fs->now ? current_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(current_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_ino_t cwd, const char *name, char *target)
178 {
179         char                    *cp;
180         ext2_ino_t              parent_ino;
181         errcode_t               retval;
182         struct ext2_inode       inode;
183         struct stat             st;
184
185         cp = strrchr(name, '/');
186         if (cp) {
187                 *cp = 0;
188                 retval = ext2fs_namei(current_fs, root, cwd, name,
189                                       &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(current_fs, parent_ino, 0, name, target);
200         if (retval == EXT2_ET_DIR_NO_SPACE) {
201                 retval = ext2fs_expand_dir(current_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 }
215
216 /* Make a directory in the fs */
217 errcode_t do_mkdir_internal(ext2_ino_t cwd, const char *name, struct stat *st)
218 {
219         char                    *cp;
220         ext2_ino_t              parent_ino, ino;
221         errcode_t               retval;
222         struct ext2_inode       inode;
223
224
225         cp = strrchr(name, '/');
226         if (cp) {
227                 *cp = 0;
228                 retval = ext2fs_namei(current_fs, root, cwd, name,
229                                       &parent_ino);
230                 if (retval) {
231                         com_err(name, retval, 0);
232                         return retval;
233                 }
234                 name = cp+1;
235         } else
236                 parent_ino = cwd;
237
238 try_again:
239         retval = ext2fs_mkdir(current_fs, parent_ino, 0, name);
240         if (retval == EXT2_ET_DIR_NO_SPACE) {
241                 retval = ext2fs_expand_dir(current_fs, parent_ino);
242                 if (retval) {
243                         com_err(__func__, retval, "while expanding directory");
244                         return retval;
245                 }
246                 goto try_again;
247         }
248         if (retval) {
249                 com_err("ext2fs_mkdir", retval, 0);
250                 return retval;
251         }
252 }
253
254 static errcode_t copy_file(int fd, ext2_ino_t newfile, int bufsize,
255                            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(current_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 int is_hardlink(ext2_ino_t ino)
334 {
335         int i;
336
337         for(i = 0; i < hdlinks.count; i++) {
338                 if(hdlinks.hdl[i].src_ino == ino)
339                         return i;
340         }
341         return -1;
342 }
343
344 /* Copy the native file to the fs */
345 errcode_t do_write_internal(ext2_ino_t cwd, const char *src, const char *dest)
346 {
347         int             fd;
348         struct stat     statbuf;
349         ext2_ino_t      newfile;
350         errcode_t       retval;
351         struct ext2_inode inode;
352         int             bufsize = IO_BUFSIZE;
353         int             make_holes = 0;
354
355         fd = open(src, O_RDONLY);
356         if (fd < 0) {
357                 com_err(src, errno, 0);
358                 return errno;
359         }
360         if (fstat(fd, &statbuf) < 0) {
361                 com_err(src, errno, 0);
362                 close(fd);
363                 return errno;
364         }
365
366         retval = ext2fs_namei(current_fs, root, cwd, dest, &newfile);
367         if (retval == 0) {
368                 close(fd);
369                 return EXT2_ET_FILE_EXISTS;
370         }
371
372         retval = ext2fs_new_inode(current_fs, cwd, 010755, 0, &newfile);
373         if (retval) {
374                 com_err(__func__, retval, 0);
375                 close(fd);
376                 return retval;
377         }
378 #ifdef DEBUGFS
379         printf("Allocated inode: %u\n", newfile);
380 #endif
381         retval = ext2fs_link(current_fs, cwd, dest, newfile,
382                                 EXT2_FT_REG_FILE);
383         if (retval == EXT2_ET_DIR_NO_SPACE) {
384                 retval = ext2fs_expand_dir(current_fs, cwd);
385                 if (retval) {
386                         com_err(__func__, retval, "while expanding directory");
387                         close(fd);
388                         return retval;
389                 }
390                 retval = ext2fs_link(current_fs, cwd, dest, newfile,
391                                         EXT2_FT_REG_FILE);
392         }
393         if (retval) {
394                 com_err(dest, retval, 0);
395                 close(fd);
396                 return errno;
397         }
398         if (ext2fs_test_inode_bitmap2(current_fs->inode_map, newfile))
399                 com_err(__func__, 0, "Warning: inode already set");
400         ext2fs_inode_alloc_stats2(current_fs, newfile, +1, 0);
401         memset(&inode, 0, sizeof(inode));
402         inode.i_mode = (statbuf.st_mode & ~LINUX_S_IFMT) | LINUX_S_IFREG;
403         inode.i_atime = inode.i_ctime = inode.i_mtime =
404                 current_fs->now ? current_fs->now : time(0);
405         inode.i_links_count = 1;
406         inode.i_size = statbuf.st_size;
407         if (EXT2_HAS_INCOMPAT_FEATURE(current_fs->super,
408                                       EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
409                 inode.i_flags |= EXT4_INLINE_DATA_FL;
410         } else if (current_fs->super->s_feature_incompat &
411                    EXT3_FEATURE_INCOMPAT_EXTENTS) {
412                 int i;
413                 struct ext3_extent_header *eh;
414
415                 eh = (struct ext3_extent_header *) &inode.i_block[0];
416                 eh->eh_depth = 0;
417                 eh->eh_entries = 0;
418                 eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC);
419                 i = (sizeof(inode.i_block) - sizeof(*eh)) /
420                         sizeof(struct ext3_extent);
421                 eh->eh_max = ext2fs_cpu_to_le16(i);
422                 inode.i_flags |= EXT4_EXTENTS_FL;
423         }
424
425         retval = ext2fs_write_new_inode(current_fs, newfile, &inode);
426         if (retval) {
427                 com_err(__func__, retval, "while creating inode %u", newfile);
428                 close(fd);
429                 return retval;
430         }
431         if (inode.i_flags & EXT4_INLINE_DATA_FL) {
432                 retval = ext2fs_inline_data_init(current_fs, newfile);
433                 if (retval) {
434                         com_err("copy_file", retval, 0);
435                         close(fd);
436                         return retval;
437                 }
438         }
439         if (LINUX_S_ISREG(inode.i_mode)) {
440                 if (statbuf.st_blocks < statbuf.st_size / S_BLKSIZE) {
441                         make_holes = 1;
442                         /*
443                          * Use I/O blocksize as buffer size when
444                          * copying sparse files.
445                          */
446                         bufsize = statbuf.st_blksize;
447                 }
448                 retval = copy_file(fd, newfile, bufsize, make_holes);
449                 if (retval)
450                         com_err("copy_file", retval, 0);
451         }
452         close(fd);
453
454         return retval;
455 }
456
457 /* Copy files from source_dir to fs */
458 errcode_t populate_fs(ext2_ino_t parent_ino, const char *source_dir)
459 {
460         const char      *name;
461         DIR             *dh;
462         struct dirent   *dent;
463         struct stat     st;
464         char            ln_target[PATH_MAX];
465         unsigned int    save_inode;
466         ext2_ino_t      ino;
467         errcode_t       retval;
468         int             read_cnt;
469         int             hdlink;
470
471         root = EXT2_ROOT_INO;
472
473         if (chdir(source_dir) < 0) {
474                 com_err(__func__, errno,
475                         _("while changing working directory to \"%s\""),
476                         source_dir);
477                 return errno;
478         }
479
480         if (!(dh = opendir("."))) {
481                 com_err(__func__, errno,
482                         _("while opening directory \"%s\""), source_dir);
483                 return errno;
484         }
485
486         while ((dent = readdir(dh))) {
487                 if ((!strcmp(dent->d_name, ".")) ||
488                     (!strcmp(dent->d_name, "..")))
489                         continue;
490                 lstat(dent->d_name, &st);
491                 name = dent->d_name;
492
493                 /* Check for hardlinks */
494                 save_inode = 0;
495                 if (!S_ISDIR(st.st_mode) && !S_ISLNK(st.st_mode) &&
496                     st.st_nlink > 1) {
497                         hdlink = is_hardlink(st.st_ino);
498                         if (hdlink >= 0) {
499                                 retval = add_link(parent_ino,
500                                                   hdlinks.hdl[hdlink].dst_ino,
501                                                   name);
502                                 if (retval) {
503                                         com_err(__func__, retval,
504                                                 "while linking %s", name);
505                                         return retval;
506                                 }
507                                 continue;
508                         } else
509                                 save_inode = 1;
510                 }
511
512                 switch(st.st_mode & S_IFMT) {
513                 case S_IFCHR:
514                 case S_IFBLK:
515                 case S_IFIFO:
516                         retval = do_mknod_internal(parent_ino, name, &st);
517                         if (retval) {
518                                 com_err(__func__, retval,
519                                         _("while creating special file "
520                                           "\"%s\""), name);
521                                 return retval;
522                         }
523                         break;
524                 case S_IFSOCK:
525                         /* FIXME: there is no make socket function atm. */
526                         com_err(__func__, 0,
527                                 _("ignoring socket file \"%s\""), name);
528                         continue;
529                 case S_IFLNK:
530                         read_cnt = readlink(name, ln_target,
531                                             sizeof(ln_target));
532                         if (read_cnt == -1) {
533                                 com_err(__func__, errno,
534                                         _("while trying to readlink \"%s\""),
535                                         name);
536                                 return errno;
537                         }
538                         ln_target[read_cnt] = '\0';
539                         retval = do_symlink_internal(parent_ino, name,
540                                                      ln_target);
541                         if (retval) {
542                                 com_err(__func__, retval,
543                                         _("while writing symlink\"%s\""),
544                                         name);
545                                 return retval;
546                         }
547                         break;
548                 case S_IFREG:
549                         retval = do_write_internal(parent_ino, name, name);
550                         if (retval) {
551                                 com_err(__func__, retval,
552                                         _("while writing file \"%s\""), name);
553                                 return retval;
554                         }
555                         break;
556                 case S_IFDIR:
557                         retval = do_mkdir_internal(parent_ino, name, &st);
558                         if (retval) {
559                                 com_err(__func__, retval,
560                                         _("while making dir \"%s\""), name);
561                                 return retval;
562                         }
563                         retval = ext2fs_namei(current_fs, root, parent_ino,
564                                               name, &ino);
565                         if (retval) {
566                                 com_err(name, retval, 0);
567                                         return retval;
568                         }
569                         /* Populate the dir recursively*/
570                         retval = populate_fs(ino, name);
571                         if (retval) {
572                                 com_err(__func__, retval,
573                                         _("while adding dir \"%s\""), name);
574                                 return retval;
575                         }
576                         chdir("..");
577                         break;
578                 default:
579                         com_err(__func__, 0,
580                                 _("ignoring entry \"%s\""), name);
581                 }
582
583                 retval =  ext2fs_namei(current_fs, root, parent_ino,
584                                        name, &ino);
585                 if (retval) {
586                         com_err(name, retval, 0);
587                         return retval;
588                 }
589
590                 retval = set_inode_extra(parent_ino, ino, &st);
591                 if (retval) {
592                         com_err(__func__, retval,
593                                 _("while setting inode for \"%s\""), name);
594                         return retval;
595                 }
596
597                 /* Save the hardlink ino */
598                 if (save_inode) {
599                         /*
600                          * Check whether need more memory, and we don't need
601                          * free() since the lifespan will be over after the fs
602                          * populated.
603                          */
604                         if (hdlinks.count == hdlink_cnt) {
605                                 if ((hdlinks.hdl = realloc (hdlinks.hdl,
606                                                 (hdlink_cnt + HDLINK_CNT) *
607                                                 sizeof (struct hdlink_s))) == NULL) {
608                                         com_err(name, errno, "Not enough memory");
609                                         return errno;
610                                 }
611                                 hdlink_cnt += HDLINK_CNT;
612                         }
613                         hdlinks.hdl[hdlinks.count].src_ino = st.st_ino;
614                         hdlinks.hdl[hdlinks.count].dst_ino = ino;
615                         hdlinks.count++;
616                 }
617         }
618         closedir(dh);
619         return retval;
620 }