Whamcloud - gitweb
misc/create_inode.c: set owner/mode/time for the inode
[tools/e2fsprogs.git] / misc / create_inode.c
1 #include "create_inode.h"
2
3 #if __STDC_VERSION__ < 199901L
4 # if __GNUC__ >= 2
5 #  define __func__ __FUNCTION__
6 # else
7 #  define __func__ "<unknown>"
8 # endif
9 #endif
10
11 /* 64KiB is the minimium blksize to best minimize system call overhead. */
12 #ifndef IO_BUFSIZE
13 #define IO_BUFSIZE 64*1024
14 #endif
15
16 /* Block size for `st_blocks' */
17 #ifndef S_BLKSIZE
18 #define S_BLKSIZE 512
19 #endif
20
21 /* Fill the uid, gid, mode and time for the inode */
22 static void fill_inode(struct ext2_inode *inode, struct stat *st)
23 {
24         if (st != NULL) {
25                 inode->i_uid = st->st_uid;
26                 inode->i_gid = st->st_gid;
27                 inode->i_mode |= st->st_mode;
28                 inode->i_atime = st->st_atime;
29                 inode->i_mtime = st->st_mtime;
30                 inode->i_ctime = st->st_ctime;
31         }
32 }
33
34 /* Set the uid, gid, mode and time for the inode */
35 errcode_t set_inode_extra(ext2_ino_t cwd, ext2_ino_t ino, struct stat *st)
36 {
37         errcode_t               retval;
38         struct ext2_inode       inode;
39
40         retval = ext2fs_read_inode(current_fs, ino, &inode);
41         if (retval) {
42                 com_err(__func__, retval, "while reading inode %u", ino);
43                 return retval;
44         }
45
46         fill_inode(&inode, st);
47
48         retval = ext2fs_write_inode(current_fs, ino, &inode);
49         if (retval) {
50                 com_err(__func__, retval, "while writing inode %u", ino);
51                 return retval;
52         }
53 }
54
55 /* Make a special file which is block, character and fifo */
56 errcode_t do_mknod_internal(ext2_ino_t cwd, const char *name, struct stat *st)
57 {
58         ext2_ino_t              ino;
59         errcode_t               retval;
60         struct ext2_inode       inode;
61         unsigned long           major, minor, mode;
62         int                     filetype;
63
64         switch(st->st_mode & S_IFMT) {
65                 case S_IFCHR:
66                         mode = LINUX_S_IFCHR;
67                         filetype = EXT2_FT_CHRDEV;
68                         break;
69                 case S_IFBLK:
70                         mode = LINUX_S_IFBLK;
71                         filetype =  EXT2_FT_BLKDEV;
72                         break;
73                 case S_IFIFO:
74                         mode = LINUX_S_IFIFO;
75                         filetype = EXT2_FT_FIFO;
76                         break;
77         }
78
79         if (!(current_fs->flags & EXT2_FLAG_RW)) {
80                 com_err(__func__, 0, "Filesystem opened read/only");
81                 return -1;
82         }
83         retval = ext2fs_new_inode(current_fs, cwd, 010755, 0, &ino);
84         if (retval) {
85                 com_err(__func__, retval, 0);
86                 return retval;
87         }
88
89 #ifdef DEBUGFS
90         printf("Allocated inode: %u\n", ino);
91 #endif
92         retval = ext2fs_link(current_fs, cwd, name, ino, filetype);
93         if (retval == EXT2_ET_DIR_NO_SPACE) {
94                 retval = ext2fs_expand_dir(current_fs, cwd);
95                 if (retval) {
96                         com_err(__func__, retval, "while expanding directory");
97                         return retval;
98                 }
99                 retval = ext2fs_link(current_fs, cwd, name, ino, filetype);
100         }
101         if (retval) {
102                 com_err(name, retval, 0);
103                 return -1;
104         }
105         if (ext2fs_test_inode_bitmap2(current_fs->inode_map, ino))
106                 com_err(__func__, 0, "Warning: inode already set");
107         ext2fs_inode_alloc_stats2(current_fs, ino, +1, 0);
108         memset(&inode, 0, sizeof(inode));
109         inode.i_mode = mode;
110         inode.i_atime = inode.i_ctime = inode.i_mtime =
111                 current_fs->now ? current_fs->now : time(0);
112
113         major = major(st->st_rdev);
114         minor = minor(st->st_rdev);
115
116         if ((major < 256) && (minor < 256)) {
117                 inode.i_block[0] = major * 256 + minor;
118                 inode.i_block[1] = 0;
119         } else {
120                 inode.i_block[0] = 0;
121                 inode.i_block[1] = (minor & 0xff) | (major << 8) | ((minor & ~0xff) << 12);
122         }
123         inode.i_links_count = 1;
124
125         retval = ext2fs_write_new_inode(current_fs, ino, &inode);
126         if (retval)
127                 com_err(__func__, retval, "while creating inode %u", ino);
128
129         return retval;
130 }
131
132 /* Make a symlink name -> target */
133 errcode_t do_symlink_internal(ext2_ino_t cwd, const char *name, char *target)
134 {
135         char                    *cp;
136         ext2_ino_t              parent_ino;
137         errcode_t               retval;
138         struct ext2_inode       inode;
139         struct stat             st;
140
141         cp = strrchr(name, '/');
142         if (cp) {
143                 *cp = 0;
144                 if ((retval =  ext2fs_namei(current_fs, root, cwd, name, &parent_ino))){
145                         com_err(name, retval, 0);
146                         return retval;
147                 }
148                 name = cp+1;
149         } else
150                 parent_ino = cwd;
151
152 try_again:
153         retval = ext2fs_symlink(current_fs, parent_ino, 0, name, target);
154         if (retval == EXT2_ET_DIR_NO_SPACE) {
155                 retval = ext2fs_expand_dir(current_fs, parent_ino);
156                 if (retval) {
157                         com_err("do_symlink_internal", retval, "while expanding directory");
158                         return retval;
159                 }
160                 goto try_again;
161         }
162         if (retval) {
163                 com_err("ext2fs_symlink", retval, 0);
164                 return retval;
165         }
166
167 }
168
169 /* Make a directory in the fs */
170 errcode_t do_mkdir_internal(ext2_ino_t cwd, const char *name, struct stat *st)
171 {
172         char                    *cp;
173         ext2_ino_t              parent_ino, ino;
174         errcode_t               retval;
175         struct ext2_inode       inode;
176
177
178         cp = strrchr(name, '/');
179         if (cp) {
180                 *cp = 0;
181                 if ((retval =  ext2fs_namei(current_fs, root, cwd, name, &parent_ino))){
182                         com_err(name, retval, 0);
183                         return retval;
184                 }
185                 name = cp+1;
186         } else
187                 parent_ino = cwd;
188
189 try_again:
190         retval = ext2fs_mkdir(current_fs, parent_ino, 0, name);
191         if (retval == EXT2_ET_DIR_NO_SPACE) {
192                 retval = ext2fs_expand_dir(current_fs, parent_ino);
193                 if (retval) {
194                         com_err(__func__, retval, "while expanding directory");
195                         return retval;
196                 }
197                 goto try_again;
198         }
199         if (retval) {
200                 com_err("ext2fs_mkdir", retval, 0);
201                 return retval;
202         }
203 }
204
205 static errcode_t copy_file(int fd, ext2_ino_t newfile, int bufsize, int make_holes)
206 {
207         ext2_file_t     e2_file;
208         errcode_t       retval;
209         int             got;
210         unsigned int    written;
211         char            *buf;
212         char            *ptr;
213         char            *zero_buf;
214         int             cmp;
215
216         retval = ext2fs_file_open(current_fs, newfile,
217                                   EXT2_FILE_WRITE, &e2_file);
218         if (retval)
219                 return retval;
220
221         retval = ext2fs_get_mem(bufsize, &buf);
222         if (retval) {
223                 com_err("copy_file", retval, "can't allocate buffer\n");
224                 return retval;
225         }
226
227         /* This is used for checking whether the whole block is zero */
228         retval = ext2fs_get_memzero(bufsize, &zero_buf);
229         if (retval) {
230                 com_err("copy_file", retval, "can't allocate buffer\n");
231                 ext2fs_free_mem(&buf);
232                 return retval;
233         }
234
235         while (1) {
236                 got = read(fd, buf, bufsize);
237                 if (got == 0)
238                         break;
239                 if (got < 0) {
240                         retval = errno;
241                         goto fail;
242                 }
243                 ptr = buf;
244
245                 /* Sparse copy */
246                 if (make_holes) {
247                         /* Check whether all is zero */
248                         cmp = memcmp(ptr, zero_buf, got);
249                         if (cmp == 0) {
250                                  /* The whole block is zero, make a hole */
251                                 retval = ext2fs_file_lseek(e2_file, got, EXT2_SEEK_CUR, NULL);
252                                 if (retval)
253                                         goto fail;
254                                 got = 0;
255                         }
256                 }
257
258                 /* Normal copy */
259                 while (got > 0) {
260                         retval = ext2fs_file_write(e2_file, ptr,
261                                                    got, &written);
262                         if (retval)
263                                 goto fail;
264
265                         got -= written;
266                         ptr += written;
267                 }
268         }
269         ext2fs_free_mem(&buf);
270         ext2fs_free_mem(&zero_buf);
271         retval = ext2fs_file_close(e2_file);
272         return retval;
273
274 fail:
275         ext2fs_free_mem(&buf);
276         ext2fs_free_mem(&zero_buf);
277         (void) ext2fs_file_close(e2_file);
278         return retval;
279 }
280
281 /* Copy the native file to the fs */
282 errcode_t do_write_internal(ext2_ino_t cwd, const char *src, const char *dest)
283 {
284         int             fd;
285         struct stat     statbuf;
286         ext2_ino_t      newfile;
287         errcode_t       retval;
288         struct ext2_inode inode;
289         int             bufsize = IO_BUFSIZE;
290         int             make_holes = 0;
291
292         fd = open(src, O_RDONLY);
293         if (fd < 0) {
294                 com_err(src, errno, 0);
295                 return errno;
296         }
297         if (fstat(fd, &statbuf) < 0) {
298                 com_err(src, errno, 0);
299                 close(fd);
300                 return errno;
301         }
302
303         retval = ext2fs_namei(current_fs, root, cwd, dest, &newfile);
304         if (retval == 0) {
305                 com_err(__func__, 0, "The file '%s' already exists\n", dest);
306                 close(fd);
307                 return errno;
308         }
309
310         retval = ext2fs_new_inode(current_fs, cwd, 010755, 0, &newfile);
311         if (retval) {
312                 com_err(__func__, retval, 0);
313                 close(fd);
314                 return errno;
315         }
316 #ifdef DEBUGFS
317         printf("Allocated inode: %u\n", newfile);
318 #endif
319         retval = ext2fs_link(current_fs, cwd, dest, newfile,
320                                 EXT2_FT_REG_FILE);
321         if (retval == EXT2_ET_DIR_NO_SPACE) {
322                 retval = ext2fs_expand_dir(current_fs, cwd);
323                 if (retval) {
324                         com_err(__func__, retval, "while expanding directory");
325                         close(fd);
326                         return errno;
327                 }
328                 retval = ext2fs_link(current_fs, cwd, dest, newfile,
329                                         EXT2_FT_REG_FILE);
330         }
331         if (retval) {
332                 com_err(dest, retval, 0);
333                 close(fd);
334                 return errno;
335         }
336         if (ext2fs_test_inode_bitmap2(current_fs->inode_map, newfile))
337                 com_err(__func__, 0, "Warning: inode already set");
338         ext2fs_inode_alloc_stats2(current_fs, newfile, +1, 0);
339         memset(&inode, 0, sizeof(inode));
340         inode.i_mode = (statbuf.st_mode & ~LINUX_S_IFMT) | LINUX_S_IFREG;
341         inode.i_atime = inode.i_ctime = inode.i_mtime =
342                 current_fs->now ? current_fs->now : time(0);
343         inode.i_links_count = 1;
344         inode.i_size = statbuf.st_size;
345         if (current_fs->super->s_feature_incompat &
346             EXT3_FEATURE_INCOMPAT_EXTENTS) {
347                 int i;
348                 struct ext3_extent_header *eh;
349
350                 eh = (struct ext3_extent_header *) &inode.i_block[0];
351                 eh->eh_depth = 0;
352                 eh->eh_entries = 0;
353                 eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC);
354                 i = (sizeof(inode.i_block) - sizeof(*eh)) /
355                         sizeof(struct ext3_extent);
356                 eh->eh_max = ext2fs_cpu_to_le16(i);
357                 inode.i_flags |= EXT4_EXTENTS_FL;
358         }
359
360         if ((retval = ext2fs_write_new_inode(current_fs, newfile, &inode))) {
361                 com_err(__func__, retval, "while creating inode %u", newfile);
362                 close(fd);
363                 return errno;
364         }
365         if (LINUX_S_ISREG(inode.i_mode)) {
366                 if (statbuf.st_blocks < statbuf.st_size / S_BLKSIZE) {
367                         make_holes = 1;
368                         /*
369                          * Use I/O blocksize as buffer size when
370                          * copying sparse files.
371                          */
372                         bufsize = statbuf.st_blksize;
373                 }
374                 retval = copy_file(fd, newfile, bufsize, make_holes);
375                 if (retval)
376                         com_err("copy_file", retval, 0);
377         }
378         close(fd);
379
380         return 0;
381 }
382
383 /* Copy files from source_dir to fs */
384 errcode_t populate_fs(ext2_ino_t parent_ino, const char *source_dir)
385 {
386         const char      *name;
387         DIR             *dh;
388         struct dirent   *dent;
389         struct stat     st;
390         char            ln_target[PATH_MAX];
391         ext2_ino_t      ino;
392         errcode_t       retval;
393         int             read_cnt;
394
395         root = EXT2_ROOT_INO;
396
397         if (chdir(source_dir) < 0) {
398                 com_err(__func__, errno,
399                         _("while changing working directory to \"%s\""), source_dir);
400                 return errno;
401         }
402
403         if (!(dh = opendir("."))) {
404                 com_err(__func__, errno,
405                         _("while openning directory \"%s\""), source_dir);
406                 return errno;
407         }
408
409         while((dent = readdir(dh))) {
410                 if((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, "..")))
411                         continue;
412                 lstat(dent->d_name, &st);
413                 name = dent->d_name;
414
415                 switch(st.st_mode & S_IFMT) {
416                         case S_IFCHR:
417                         case S_IFBLK:
418                         case S_IFIFO:
419                                 if ((retval = do_mknod_internal(parent_ino, name, &st))) {
420                                         com_err(__func__, retval,
421                                                 _("while creating special file \"%s\""), name);
422                                         return retval;
423                                 }
424                                 break;
425                         case S_IFSOCK:
426                                 /* FIXME: there is no make socket function atm. */
427                                 com_err(__func__, 0,
428                                         _("ignoring socket file \"%s\""), name);
429                                 continue;
430                         case S_IFLNK:
431                                 if((read_cnt = readlink(name, ln_target, sizeof(ln_target))) == -1) {
432                                         com_err(__func__, errno,
433                                                 _("while trying to readlink \"%s\""), name);
434                                         return errno;
435                                 }
436                                 ln_target[read_cnt] = '\0';
437                                 if ((retval = do_symlink_internal(parent_ino, name, ln_target))) {
438                                         com_err(__func__, retval,
439                                                 _("while writing symlink\"%s\""), name);
440                                         return retval;
441                                 }
442                                 break;
443                         case S_IFREG:
444                                 if ((retval = do_write_internal(parent_ino, name, name))) {
445                                         com_err(__func__, retval,
446                                                 _("while writing file \"%s\""), name);
447                                         return retval;
448                                 }
449                                 break;
450                         case S_IFDIR:
451                                 if ((retval = do_mkdir_internal(parent_ino, name, &st))) {
452                                         com_err(__func__, retval,
453                                                 _("while making dir \"%s\""), name);
454                                         return retval;
455                                 }
456                                 if ((retval = ext2fs_namei(current_fs, root, parent_ino, name, &ino))) {
457                                         com_err(name, retval, 0);
458                                                 return retval;
459                                 }
460                                 /* Populate the dir recursively*/
461                                 retval = populate_fs(ino, name);
462                                 if (retval) {
463                                         com_err(__func__, retval, _("while adding dir \"%s\""), name);
464                                         return retval;
465                                 }
466                                 chdir("..");
467                                 break;
468                         default:
469                                 com_err(__func__, 0,
470                                         _("ignoring entry \"%s\""), name);
471                 }
472
473                 if ((retval =  ext2fs_namei(current_fs, root, parent_ino, name, &ino))){
474                         com_err(name, retval, 0);
475                         return retval;
476                 }
477
478                 if ((retval = set_inode_extra(parent_ino, ino, &st))) {
479                         com_err(__func__, retval,
480                                 _("while setting inode for \"%s\""), name);
481                         return retval;
482                 }
483         }
484         closedir(dh);
485         return retval;
486 }