link.c 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * link.c --- create links in a ext2fs directory
  4. *
  5. * Copyright (C) 1993, 1994 Theodore Ts'o.
  6. *
  7. * %Begin-Header%
  8. * This file may be redistributed under the terms of the GNU Public
  9. * License.
  10. * %End-Header%
  11. */
  12. #include <stdio.h>
  13. #include <string.h>
  14. #if HAVE_UNISTD_H
  15. #include <unistd.h>
  16. #endif
  17. #include "ext2_fs.h"
  18. #include "ext2fs.h"
  19. struct link_struct {
  20. const char *name;
  21. int namelen;
  22. ext2_ino_t inode;
  23. int flags;
  24. int done;
  25. struct ext2_super_block *sb;
  26. };
  27. static int link_proc(struct ext2_dir_entry *dirent,
  28. int offset,
  29. int blocksize,
  30. char *buf,
  31. void *priv_data)
  32. {
  33. struct link_struct *ls = (struct link_struct *) priv_data;
  34. struct ext2_dir_entry *next;
  35. int rec_len, min_rec_len;
  36. int ret = 0;
  37. rec_len = EXT2_DIR_REC_LEN(ls->namelen);
  38. /*
  39. * See if the following directory entry (if any) is unused;
  40. * if so, absorb it into this one.
  41. */
  42. next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len);
  43. if ((offset + dirent->rec_len < blocksize - 8) &&
  44. (next->inode == 0) &&
  45. (offset + dirent->rec_len + next->rec_len <= blocksize)) {
  46. dirent->rec_len += next->rec_len;
  47. ret = DIRENT_CHANGED;
  48. }
  49. /*
  50. * If the directory entry is used, see if we can split the
  51. * directory entry to make room for the new name. If so,
  52. * truncate it and return.
  53. */
  54. if (dirent->inode) {
  55. min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF);
  56. if (dirent->rec_len < (min_rec_len + rec_len))
  57. return ret;
  58. rec_len = dirent->rec_len - min_rec_len;
  59. dirent->rec_len = min_rec_len;
  60. next = (struct ext2_dir_entry *) (buf + offset +
  61. dirent->rec_len);
  62. next->inode = 0;
  63. next->name_len = 0;
  64. next->rec_len = rec_len;
  65. return DIRENT_CHANGED;
  66. }
  67. /*
  68. * If we get this far, then the directory entry is not used.
  69. * See if we can fit the request entry in. If so, do it.
  70. */
  71. if (dirent->rec_len < rec_len)
  72. return ret;
  73. dirent->inode = ls->inode;
  74. dirent->name_len = ls->namelen;
  75. strncpy(dirent->name, ls->name, ls->namelen);
  76. if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
  77. dirent->name_len |= (ls->flags & 0x7) << 8;
  78. ls->done++;
  79. return DIRENT_ABORT|DIRENT_CHANGED;
  80. }
  81. /*
  82. * Note: the low 3 bits of the flags field are used as the directory
  83. * entry filetype.
  84. */
  85. #ifdef __TURBOC__
  86. # pragma argsused
  87. #endif
  88. errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
  89. ext2_ino_t ino, int flags)
  90. {
  91. errcode_t retval;
  92. struct link_struct ls;
  93. struct ext2_inode inode;
  94. EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
  95. if (!(fs->flags & EXT2_FLAG_RW))
  96. return EXT2_ET_RO_FILSYS;
  97. ls.name = name;
  98. ls.namelen = name ? strlen(name) : 0;
  99. ls.inode = ino;
  100. ls.flags = flags;
  101. ls.done = 0;
  102. ls.sb = fs->super;
  103. retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY,
  104. 0, link_proc, &ls);
  105. if (retval)
  106. return retval;
  107. if (!ls.done)
  108. return EXT2_ET_DIR_NO_SPACE;
  109. if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0)
  110. return retval;
  111. if (inode.i_flags & EXT2_INDEX_FL) {
  112. inode.i_flags &= ~EXT2_INDEX_FL;
  113. if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0)
  114. return retval;
  115. }
  116. return 0;
  117. }