Whamcloud - gitweb
LU-8300 e2fsck: print out FID with dirent inode number
[tools/e2fsprogs.git] / e2fsck / message.c
index 7622dcb..eca280a 100644 (file)
  * The following % expansions are supported:
  *
  *     %b      <blk>                   block number
- *     %B      <blkcount>              integer
+ *     %B      <blkcount>              interpret blkcount as blkcount
  *     %c      <blk2>                  block number
- *     %di     <dirent>->ino           inode number
- *     %dn     <dirent>->name          string
- *     %dr     <dirent>->rec_len
- *     %dl     <dirent>->name_len
- *     %dt     <dirent>->filetype
- *     %D      <dir>                   inode number
+ *     %Di     <dirent>->ino           inode number
+ *     %Dn     <dirent>->name          string
+ *     %Dr     <dirent>->rec_len
+ *     %Dl     <dirent>->name_len
+ *     %Dt     <dirent>->filetype
+ *     %d      <dir>                   inode number
  *     %g      <group>                 integer
  *     %i      <ino>                   inode number
  *     %Is     <inode> -> i_size
+ *     %IS     <inode> -> i_extra_isize
  *     %Ib     <inode> -> i_blocks
  *     %Il     <inode> -> i_links_count
  *     %Im     <inode> -> i_mode
  *     %IM     <inode> -> i_mtime
  *     %IF     <inode> -> i_faddr
  *     %If     <inode> -> i_file_acl
- *     %Id     <inode> -> i_dir_acl
+ *     %Id     <inode> -> i_size_high
+ *     %Iu     <inode> -> i_uid
+ *     %Ig     <inode> -> i_gid
+ *     %It     <inode type>
  *     %j      <ino2>                  inode number
  *     %m      <com_err error message>
  *     %N      <num>
  *     %q      ext2fs_get_pathname of directory <dir>
  *     %Q      ext2fs_get_pathname of directory <ino> with <dir> as
  *                     the containing directory.
+ *     %r      <blkcount>              interpret blkcount as refcount
  *     %s      <str>                   miscellaneous string
+ *     %t      time (in <num>)
+ *     %T      current time
+ *     %U      quota type (in <num>)
  *     %S      backup superblock
  *     %X      <num> hexadecimal format
  *
  * The following '@' expansions are supported:
  *
+ *     @a      extended attribute
  *     @A      error allocating
  *     @b      block
  *     @B      bitmap
  *     @c      compress
  *     @C      conflicts with some other fs block
- *     @i      inode
- *     @I      illegal
  *     @D      deleted
  *     @d      directory
  *     @e      entry
  *     @f      filesystem
  *     @F      for @i %i (%Q) is
  *     @g      group
+ *     @h      HTREE directory inode
+ *     @i      inode
+ *     @I      illegal
+ *     @j      journal
  *     @l      lost+found
  *     @L      is a link
+ *     @m      multiply-claimed
+ *     @n      invalid
  *     @o      orphaned
+ *     @p      problem in
+ *     @q      quota
  *     @r      root inode
- *     @s      should be 
+ *     @s      should be
  *     @S      superblock
  *     @u      unattached
+ *     @v      device
+ *     @x      extent
  *     @z      zero-length
  */
 
+#include "config.h"
 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <ctype.h>
 #include <termios.h>
+#include "support/quotaio.h"
 
 #include "e2fsck.h"
-
 #include "problem.h"
+#include "ext2fs/lfsck.h"
 
 #ifdef __GNUC__
 #define _INLINE_ __inline__
  * letter <i> in the table below.
  */
 static const char *abbrevs[] = {
+       N_("aextended attribute"),
        N_("Aerror allocating"),
        N_("bblock"),
        N_("Bbitmap"),
        N_("ccompress"),
        N_("Cconflicts with some other fs @b"),
-       N_("iinode"),
-       N_("Iillegal"),
-       N_("Ddeleted"),
        N_("ddirectory"),
+       N_("Ddeleted"),
        N_("eentry"),
        N_("E@e '%Dn' in %p (%i)"),
        N_("ffilesystem"),
        N_("Ffor @i %i (%Q) is"),
        N_("ggroup"),
+       N_("hHTREE @d @i"),
+       N_("iinode"),
+       N_("Iillegal"),
+       N_("jjournal"),
        N_("llost+found"),
        N_("Lis a link"),
+       N_("mmultiply-claimed"),
+       N_("ninvalid"),
        N_("oorphaned"),
+       N_("pproblem in"),
+       N_("qquota"),
        N_("rroot @i"),
        N_("sshould be"),
        N_("Ssuper@b"),
        N_("uunattached"),
+       N_("vdevice"),
+       N_("xextent"),
        N_("zzero-length"),
        "@@",
        0
@@ -124,23 +152,27 @@ static const char *abbrevs[] = {
 /*
  * Give more user friendly names to the "special" inodes.
  */
-#define num_special_inodes     7
+#define num_special_inodes     11
 static const char *special_inode_name[] =
 {
        N_("<The NULL inode>"),                 /* 0 */
        N_("<The bad blocks inode>"),           /* 1 */
        "/",                                    /* 2 */
-       N_("<The ACL index inode>"),            /* 3 */
-       N_("<The ACL data inode>"),             /* 4 */
+       N_("<The user quota inode>"),           /* 3 */
+       N_("<The group quota inode>"),          /* 4 */
        N_("<The boot loader inode>"),          /* 5 */
-       N_("<The undelete directory inode>")    /* 6 */
+       N_("<The undelete directory inode>"),   /* 6 */
+       N_("<The group descriptor inode>"),     /* 7 */
+       N_("<The journal inode>"),              /* 8 */
+       N_("<Reserved inode 9>"),               /* 9 */
+       N_("<Reserved inode 10>"),              /* 10 */
 };
 
 /*
  * This function does "safe" printing.  It will convert non-printable
  * ASCII characters using '^' and M- notation.
  */
-static void safe_print(const char *cp, int len)
+static void safe_print(FILE *f, const char *cp, int len)
 {
        unsigned char   ch;
 
@@ -150,14 +182,14 @@ static void safe_print(const char *cp, int len)
        while (len--) {
                ch = *cp++;
                if (ch > 128) {
-                       fputs("M-", stdout);
+                       fputs("M-", f);
                        ch -= 128;
                }
                if ((ch < 32) || (ch == 0x7f)) {
-                       fputc('^', stdout);
+                       fputc('^', f);
                        ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
                }
-               fputc(ch, stdout);
+               fputc(ch, f);
        }
 }
 
@@ -166,261 +198,393 @@ static void safe_print(const char *cp, int len)
  * This function prints a pathname, using the ext2fs_get_pathname
  * function
  */
-static void print_pathname(ext2_filsys fs, ino_t dir, ino_t ino)
+static void print_pathname(FILE *f, ext2_filsys fs, ext2_ino_t dir,
+                          ext2_ino_t ino)
 {
-       errcode_t       retval;
+       errcode_t       retval = 0;
        char            *path;
 
        if (!dir && (ino < num_special_inodes)) {
-               fputs(_(special_inode_name[ino]), stdout);
+               fputs(_(special_inode_name[ino]), f);
                return;
        }
-       
-       retval = ext2fs_get_pathname(fs, dir, ino, &path);
-       if (retval)
-               fputs("???", stdout);
+
+       if (fs)
+               retval = ext2fs_get_pathname(fs, dir, ino, &path);
+       if (!fs || retval)
+               fputs("???", f);
        else {
-               safe_print(path, -1);
-               ext2fs_free_mem((void **) &path);
+               safe_print(f, path, -1);
+               ext2fs_free_mem(&path);
        }
 }
 
+static void print_time(FILE *f, time_t t)
+{
+       const char *            time_str;
+       static int              do_gmt = -1;
+
+#ifdef __dietlibc__
+               /* The diet libc doesn't respect the TZ environment variable */
+               if (do_gmt == -1) {
+                       time_str = getenv("TZ");
+                       if (!time_str)
+                               time_str = "";
+                       do_gmt = !strcmp(time_str, "GMT") ||
+                               !strcmp(time_str, "GMT0");
+               }
+#endif
+               time_str = asctime((do_gmt > 0) ? gmtime(&t) : localtime(&t));
+               fprintf(f, "%.24s", time_str);
+}
+
 /*
  * This function handles the '@' expansion.  We allow recursive
  * expansion; an @ expression can contain further '@' and '%'
- * expressions. 
+ * expressions.
  */
-static _INLINE_ void expand_at_expression(e2fsck_t ctx, char ch,
+static _INLINE_ void expand_at_expression(FILE *f, e2fsck_t ctx, char ch,
                                          struct problem_context *pctx,
-                                         int *first)
+                                         int *first, int recurse)
 {
        const char **cpp, *str;
-       
+
        /* Search for the abbreviation */
        for (cpp = abbrevs; *cpp; cpp++) {
                if (ch == *cpp[0])
                        break;
        }
-       if (*cpp) {
-               str = (*cpp) + 1;
+       if (*cpp && recurse < 10) {
+               str = _(*cpp) + 1;
                if (*first && islower(*str)) {
                        *first = 0;
-                       fputc(toupper(*str++), stdout);
+                       fputc(toupper(*str++), f);
                }
-               print_e2fsck_message(ctx, _(str), pctx, *first);
+               print_e2fsck_message(f, ctx, str, pctx, *first, recurse+1);
        } else
-               printf("@%c", ch);
+               fprintf(f, "@%c", ch);
 }
 
 /*
- * This function expands '%kX' expressions
+ * This function expands '%IX' expressions
  */
-static _INLINE_ void expand_inode_expression(char ch,
-                                              struct problem_context *ctx)
+static _INLINE_ void expand_inode_expression(FILE *f, ext2_filsys fs, char ch,
+                                            struct problem_context *ctx)
 {
        struct ext2_inode       *inode;
-       char *                  time_str;
-       time_t                  t;
+       struct ext2_inode_large *large_inode;
 
        if (!ctx || !ctx->inode)
                goto no_inode;
-       
+
        inode = ctx->inode;
-       
+       large_inode = (struct ext2_inode_large *) inode;
+
        switch (ch) {
        case 's':
                if (LINUX_S_ISDIR(inode->i_mode))
-                       printf("%u", inode->i_size);
-               else {
-#ifdef EXT2_NO_64_TYPE
-                       if (inode->i_size_high)
-                               printf("0x%x%08x", inode->i_size_high,
-                                      inode->i_size);
-                       else
-                               printf("%u", inode->i_size);
-#else
-                       printf("%llu", (inode->i_size | 
-                                       ((__u64) inode->i_size_high << 32)));
-#endif
-               }
+                       fprintf(f, "%u", inode->i_size);
+               else
+                       fprintf(f, "%llu", EXT2_I_SIZE(inode));
+               break;
+       case 'S':
+               fprintf(f, "%u", large_inode->i_extra_isize);
                break;
        case 'b':
-               printf("%u", inode->i_blocks);
+               if (ext2fs_has_feature_huge_file(fs->super))
+                       fprintf(f, "%llu", inode->i_blocks +
+                               (((long long) inode->osd2.linux2.l_i_blocks_hi)
+                                << 32));
+               else
+                       fprintf(f, "%u", inode->i_blocks);
                break;
        case 'l':
-               printf("%d", inode->i_links_count);
+               fprintf(f, "%d", inode->i_links_count);
                break;
        case 'm':
-               printf("0%o", inode->i_mode);
+               fprintf(f, "0%o", inode->i_mode);
                break;
        case 'M':
-               t = inode->i_mtime;
-               time_str = ctime(&t);
-               printf("%.24s", time_str);
+               print_time(f, inode->i_mtime);
                break;
        case 'F':
-               printf("%u", inode->i_faddr);
+               fprintf(f, "%u", inode->i_faddr);
                break;
        case 'f':
-               printf("%u", inode->i_file_acl);
+               fprintf(f, "%llu", ext2fs_file_acl_block(fs, inode));
                break;
        case 'd':
-               printf("%u", (LINUX_S_ISDIR(inode->i_mode) ?
-                             inode->i_dir_acl : 0));
+               fprintf(f, "%u", (LINUX_S_ISDIR(inode->i_mode) ?
+                       inode->i_size_high : 0));
+               break;
+       case 'u':
+               fprintf(f, "%d", inode_uid(*inode));
+               break;
+       case 'g':
+               fprintf(f, "%d", inode_gid(*inode));
+               break;
+       case 't':
+               if (LINUX_S_ISREG(inode->i_mode))
+                       fputs(_("regular file"), f);
+               else if (LINUX_S_ISDIR(inode->i_mode))
+                       fputs(_("directory"), f);
+               else if (LINUX_S_ISCHR(inode->i_mode))
+                       fputs(_("character device"), f);
+               else if (LINUX_S_ISBLK(inode->i_mode))
+                       fputs(_("block device"), f);
+               else if (LINUX_S_ISFIFO(inode->i_mode))
+                       fputs(_("named pipe"), f);
+               else if (LINUX_S_ISLNK(inode->i_mode))
+                       fputs(_("symbolic link"), f);
+               else if (LINUX_S_ISSOCK(inode->i_mode))
+                       fputs(_("socket"), f);
+               else
+                       fprintf(f, _("unknown file type with mode 0%o"),
+                               inode->i_mode);
                break;
        default:
        no_inode:
-               printf("%%I%c", ch);
+               fprintf(f, "%%I%c", ch);
                break;
        }
 }
 
+const struct lu_fid *get_dirent_fid(struct ext2_dir_entry *dirent)
+{
+       unsigned char *data = (unsigned char *)dirent->name +
+                             (dirent->name_len & EXT2_NAME_LEN) + 1;
+       __u8 file_type = dirent->name_len >> 8;
+       struct lu_fid *fid = NULL;
+       __u8 dirdata_mask;
+
+       for (dirdata_mask = EXT2_FT_MASK + 1;
+            dirdata_mask != 0; dirdata_mask <<= 1) {
+               int dlen;
+
+               if ((dirdata_mask & file_type) == 0)
+                       continue;
+
+               dlen = data[0];
+               if (dirdata_mask == EXT2_DIRENT_LUFID) {
+                       fid = (struct lu_fid *)(data + 1);
+                       fid_be_to_cpu(fid, fid);
+                       break;
+               }
+               data += dlen;
+       }
+
+       return fid;
+}
+
 /*
  * This function expands '%dX' expressions
  */
-static _INLINE_ void expand_dirent_expression(char ch,
+static _INLINE_ void expand_dirent_expression(FILE *f, ext2_filsys fs, char ch,
                                              struct problem_context *ctx)
 {
        struct ext2_dir_entry   *dirent;
-       int     len;
-       
+       unsigned int rec_len, len;
+
        if (!ctx || !ctx->dirent)
                goto no_dirent;
-       
+
        dirent = ctx->dirent;
-       
+
        switch (ch) {
-       case 'i':
-               printf("%u", dirent->inode);
+       case 'i': {
+               const struct lu_fid *fid;
+
+               fprintf(f, "%u", dirent->inode);
+
+               fid = get_dirent_fid(dirent);
+               if (fid != NULL)
+                       fprintf(f, " fid="DFID, PFID(fid));
+
                break;
+       }
        case 'n':
-               len = dirent->name_len & 0xFF;
-               if (len > EXT2_NAME_LEN)
-                       len = EXT2_NAME_LEN;
-               if (len > dirent->rec_len)
-                       len = dirent->rec_len;
-               safe_print(dirent->name, len);
+               len = ext2fs_dirent_name_len(dirent);
+               if ((ext2fs_get_rec_len(fs, dirent, &rec_len) == 0) &&
+                   (len > rec_len))
+                       len = rec_len;
+               safe_print(f, dirent->name, len);
                break;
        case 'r':
-               printf("%u", dirent->rec_len);
+               (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
+               fprintf(f, "%u", rec_len);
                break;
        case 'l':
-               printf("%u", dirent->name_len & 0xFF);
+               fprintf(f, "%u", ext2fs_dirent_name_len(dirent));
                break;
        case 't':
-               printf("%u", dirent->name_len >> 8);
+               fprintf(f, "%u", ext2fs_dirent_file_type(dirent));
                break;
        default:
        no_dirent:
-               printf("%%D%c", ch);
+               fprintf(f, "%%D%c", ch);
                break;
        }
 }
 
-static _INLINE_ void expand_percent_expression(ext2_filsys fs, char ch,
+static _INLINE_ void expand_percent_expression(FILE *f, ext2_filsys fs,
+                                              char ch, int width, int *first,
                                               struct problem_context *ctx)
 {
+       e2fsck_t e2fsck_ctx = fs ? (e2fsck_t) fs->priv_data : NULL;
+       const char *m;
+
        if (!ctx)
                goto no_context;
-       
+
        switch (ch) {
        case '%':
-               fputc('%', stdout);
+               fputc('%', f);
                break;
        case 'b':
-               printf("%u", ctx->blk);
+               fprintf(f, "%*llu", width, (unsigned long long) ctx->blk);
                break;
        case 'B':
-#ifdef EXT2_NO_64_TYPE
-               printf("%d", ctx->blkcount);
-#else
-               printf("%lld", ctx->blkcount);
-#endif
+               if (ctx->blkcount == BLOCK_COUNT_IND)
+                       m = _("indirect block");
+               else if (ctx->blkcount == BLOCK_COUNT_DIND)
+                       m = _("double indirect block");
+               else if (ctx->blkcount == BLOCK_COUNT_TIND)
+                       m = _("triple indirect block");
+               else if (ctx->blkcount == BLOCK_COUNT_TRANSLATOR)
+                       m = _("translator block");
+               else
+                       m = _("block #");
+               if (*first && islower(m[0]))
+                       fputc(toupper(*m++), f);
+               fputs(m, f);
+               if (ctx->blkcount >= 0)
+                       fprintf(f, "%lld", (long long) ctx->blkcount);
                break;
        case 'c':
-               printf("%u", ctx->blk2);
+               fprintf(f, "%*llu", width, (unsigned long long) ctx->blk2);
                break;
        case 'd':
-               printf("%lu", ctx->dir);
+               fprintf(f, "%*u", width, ctx->dir);
                break;
        case 'g':
-               printf("%d", ctx->group);
+               fprintf(f, "%*u", width, ctx->group);
                break;
        case 'i':
-               printf("%lu", ctx->ino);
+               fprintf(f, "%*u", width, ctx->ino);
                break;
        case 'j':
-               printf("%lu", ctx->ino2);
+               fprintf(f, "%*u", width, ctx->ino2);
                break;
        case 'm':
-               printf("%s", error_message(ctx->errcode));
+               fprintf(f, "%*s", width, error_message(ctx->errcode));
                break;
        case 'N':
-#ifdef EXT2_NO_64_TYPE
-               printf("%u", ctx->num);
-#else
-               printf("%llu", ctx->num);
-#endif
+               fprintf(f, "%*llu", width, (long long)ctx->num);
+               break;
+       case 'n':
+               fprintf(f, "%*llu", width, (long long)ctx->num2);
                break;
        case 'p':
-               print_pathname(fs, ctx->ino, 0);
+               print_pathname(f, fs, ctx->ino, 0);
                break;
        case 'P':
-               print_pathname(fs, ctx->ino2,
+               print_pathname(f, fs, ctx->ino2,
                               ctx->dirent ? ctx->dirent->inode : 0);
                break;
        case 'q':
-               print_pathname(fs, ctx->dir, 0);
+               print_pathname(f, fs, ctx->dir, 0);
                break;
        case 'Q':
-               print_pathname(fs, ctx->dir, ctx->ino);
+               print_pathname(f, fs, ctx->dir, ctx->ino);
+               break;
+       case 'r':
+               fprintf(f, "%*lld", width, (long long) ctx->blkcount);
                break;
        case 'S':
-               printf("%d", get_backup_sb(fs));
+               fprintf(f, "%llu", get_backup_sb(NULL, fs, NULL, NULL));
                break;
        case 's':
-               printf("%s", ctx->str);
+               fprintf(f, "%*s", width, ctx->str ? ctx->str : "NULL");
+               break;
+       case 't':
+               print_time(f, (time_t) ctx->num);
+               break;
+       case 'T':
+               print_time(f, e2fsck_ctx ? e2fsck_ctx->now : time(0));
+               break;
+       case 'U':
+               switch (ctx->num) {
+               case USRQUOTA:
+                       m = _("user");
+                       break;
+               case GRPQUOTA:
+                       m = _("group");
+                       break;
+               case PRJQUOTA:
+                       m = _("project");
+                       break;
+               default:
+                       m = _("unknown quota type");
+                       break;
+               }
+               if (*first && islower(m[0]))
+                       fputc(toupper(*m++), f);
+               fputs(m, f);
+               if (ctx->num > PRJQUOTA)
+                       fprintf(f, " %d", (int) ctx->num);
+               break;
+       case 'x':
+               fprintf(f, "0x%0*x", width, ctx->csum1);
                break;
        case 'X':
-#ifdef EXT2_NO_64_TYPE
-               printf("0x%x", ctx->num);
-#else
-               printf("0x%llx", ctx->num);
-#endif
+               fprintf(f, "0x%0*llx", width, (long long)ctx->num);
+               break;
+       case 'y':
+               fprintf(f, "0x%0*x", width, ctx->csum2);
                break;
        default:
        no_context:
-               printf("%%%c", ch);
+               fprintf(f, "%%%c", ch);
                break;
        }
-}      
+}
 
-void print_e2fsck_message(e2fsck_t ctx, const char *msg,
-                         struct problem_context *pctx, int first)
+void print_e2fsck_message(FILE *f, e2fsck_t ctx, const char *msg,
+                         struct problem_context *pctx, int first,
+                         int recurse)
 {
        ext2_filsys fs = ctx->fs;
        const char *    cp;
-       int             i;
+       int             i, width;
 
        e2fsck_clear_progbar(ctx);
        for (cp = msg; *cp; cp++) {
                if (cp[0] == '@') {
                        cp++;
-                       expand_at_expression(ctx, *cp, pctx, &first);
-               } else if (cp[0] == '%' && cp[1] == 'I') {
-                       cp += 2;
-                       expand_inode_expression(*cp, pctx);
-               } else if (cp[0] == '%' && cp[1] == 'D') {
-                       cp += 2;
-                       expand_dirent_expression(*cp, pctx);
-               } else if ((cp[0] == '%')) {
+                       expand_at_expression(f, ctx, *cp, pctx, &first,
+                                            recurse);
+               } else if (cp[0] == '%') {
                        cp++;
-                       expand_percent_expression(fs, *cp, pctx);
+                       width = 0;
+                       while (isdigit(cp[0])) {
+                               width = (width * 10) + cp[0] - '0';
+                               cp++;
+                       }
+                       if (cp[0] == 'I') {
+                               cp++;
+                               expand_inode_expression(f, fs, *cp, pctx);
+                       } else if (cp[0] == 'D') {
+                               cp++;
+                               expand_dirent_expression(f, fs, *cp, pctx);
+                       } else {
+                               expand_percent_expression(f, fs, *cp, width,
+                                                         &first, pctx);
+                       }
                } else {
                        for (i=0; cp[i]; i++)
                                if ((cp[i] == '@') || cp[i] == '%')
                                        break;
-                       printf("%.*s", i, cp);
+                       fprintf(f, "%.*s", i, cp);
                        cp += i-1;
                }
                first = 0;