chattr.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * chattr.c - Change file attributes on an ext2 file system
  4. *
  5. * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
  6. * Laboratoire MASI, Institut Blaise Pascal
  7. * Universite Pierre et Marie Curie (Paris VI)
  8. *
  9. * This file can be redistributed under the terms of the GNU General
  10. * Public License
  11. */
  12. /*
  13. * History:
  14. * 93/10/30 - Creation
  15. * 93/11/13 - Replace stat() calls by lstat() to avoid loops
  16. * 94/02/27 - Integrated in Ted's distribution
  17. * 98/12/29 - Ignore symlinks when working recursively (G M Sipe)
  18. * 98/12/29 - Display version info only when -V specified (G M Sipe)
  19. */
  20. //usage:#define chattr_trivial_usage
  21. //usage: "[-R] [-+=AacDdijsStTu] [-v VERSION] [FILE]..."
  22. //usage:#define chattr_full_usage "\n\n"
  23. //usage: "Change file attributes on an ext2 fs\n"
  24. //usage: "\nModifiers:"
  25. //usage: "\n - Remove attributes"
  26. //usage: "\n + Add attributes"
  27. //usage: "\n = Set attributes"
  28. //usage: "\nAttributes:"
  29. //usage: "\n A Don't track atime"
  30. //usage: "\n a Append mode only"
  31. //usage: "\n c Enable compress"
  32. //usage: "\n D Write dir contents synchronously"
  33. //usage: "\n d Don't backup with dump"
  34. //usage: "\n i Cannot be modified (immutable)"
  35. //usage: "\n j Write all data to journal first"
  36. //usage: "\n s Zero disk storage when deleted"
  37. //usage: "\n S Write file contents synchronously"
  38. //usage: "\n t Disable tail-merging of partial blocks with other files"
  39. //usage: "\n u Allow file to be undeleted"
  40. //usage: "\n -R Recurse"
  41. //usage: "\n -v Set the file's version/generation number"
  42. #include "libbb.h"
  43. #include "e2fs_lib.h"
  44. #define OPT_ADD 1
  45. #define OPT_REM 2
  46. #define OPT_SET 4
  47. #define OPT_SET_VER 8
  48. struct globals {
  49. unsigned long version;
  50. unsigned long af;
  51. unsigned long rf;
  52. smallint flags;
  53. smallint recursive;
  54. };
  55. static unsigned long get_flag(char c)
  56. {
  57. const char *fp = strchr(e2attr_flags_sname_chattr, c);
  58. if (fp)
  59. return e2attr_flags_value_chattr[fp - e2attr_flags_sname_chattr];
  60. bb_show_usage();
  61. }
  62. static int decode_arg(const char *arg, struct globals *gp)
  63. {
  64. unsigned long *fl;
  65. char opt = *arg++;
  66. fl = &gp->af;
  67. if (opt == '-') {
  68. gp->flags |= OPT_REM;
  69. fl = &gp->rf;
  70. } else if (opt == '+') {
  71. gp->flags |= OPT_ADD;
  72. } else if (opt == '=') {
  73. gp->flags |= OPT_SET;
  74. } else
  75. return 0;
  76. while (*arg)
  77. *fl |= get_flag(*arg++);
  78. return 1;
  79. }
  80. static void change_attributes(const char *name, struct globals *gp);
  81. static int FAST_FUNC chattr_dir_proc(const char *dir_name, struct dirent *de, void *gp)
  82. {
  83. char *path = concat_subpath_file(dir_name, de->d_name);
  84. /* path is NULL if de->d_name is "." or "..", else... */
  85. if (path) {
  86. change_attributes(path, gp);
  87. free(path);
  88. }
  89. return 0;
  90. }
  91. static void change_attributes(const char *name, struct globals *gp)
  92. {
  93. unsigned long fsflags;
  94. struct stat st;
  95. if (lstat(name, &st) != 0) {
  96. bb_perror_msg("stat %s", name);
  97. return;
  98. }
  99. if (S_ISLNK(st.st_mode) && gp->recursive)
  100. return;
  101. /* Don't try to open device files, fifos etc. We probably
  102. * ought to display an error if the file was explicitly given
  103. * on the command line (whether or not recursive was
  104. * requested). */
  105. if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
  106. return;
  107. if (gp->flags & OPT_SET_VER)
  108. if (fsetversion(name, gp->version) != 0)
  109. bb_perror_msg("setting version on %s", name);
  110. if (gp->flags & OPT_SET) {
  111. fsflags = gp->af;
  112. } else {
  113. if (fgetflags(name, &fsflags) != 0) {
  114. bb_perror_msg("reading flags on %s", name);
  115. goto skip_setflags;
  116. }
  117. /*if (gp->flags & OPT_REM) - not needed, rf is zero otherwise */
  118. fsflags &= ~gp->rf;
  119. /*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */
  120. fsflags |= gp->af;
  121. /* What is this? And why it's not done for SET case? */
  122. if (!S_ISDIR(st.st_mode))
  123. fsflags &= ~EXT2_DIRSYNC_FL;
  124. }
  125. if (fsetflags(name, fsflags) != 0)
  126. bb_perror_msg("setting flags on %s", name);
  127. skip_setflags:
  128. if (gp->recursive && S_ISDIR(st.st_mode))
  129. iterate_on_dir(name, chattr_dir_proc, gp);
  130. }
  131. int chattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  132. int chattr_main(int argc UNUSED_PARAM, char **argv)
  133. {
  134. struct globals g;
  135. char *arg;
  136. memset(&g, 0, sizeof(g));
  137. /* parse the args */
  138. while ((arg = *++argv)) {
  139. /* take care of -R and -v <version> */
  140. if (arg[0] == '-'
  141. && (arg[1] == 'R' || arg[1] == 'v')
  142. && !arg[2]
  143. ) {
  144. if (arg[1] == 'R') {
  145. g.recursive = 1;
  146. continue;
  147. }
  148. /* arg[1] == 'v' */
  149. if (!*++argv)
  150. bb_show_usage();
  151. g.version = xatoul(*argv);
  152. g.flags |= OPT_SET_VER;
  153. continue;
  154. }
  155. if (!decode_arg(arg, &g))
  156. break;
  157. }
  158. /* run sanity checks on all the arguments given us */
  159. if (!*argv)
  160. bb_show_usage();
  161. if ((g.flags & OPT_SET) && (g.flags & (OPT_ADD|OPT_REM)))
  162. bb_error_msg_and_die("= is incompatible with - and +");
  163. if (g.rf & g.af)
  164. bb_error_msg_and_die("can't set and unset a flag");
  165. if (!g.flags)
  166. bb_error_msg_and_die("must use '-v', =, - or +");
  167. /* now run chattr on all the files passed to us */
  168. do change_attributes(*argv, &g); while (*++argv);
  169. return EXIT_SUCCESS;
  170. }