|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Copyright (c) 2000-2003 Silicon Graphics, Inc. | 
|  | * All Rights Reserved. | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * | 
|  | * loggen: Generate log entries. Very much incomplete. The empty log | 
|  | *         record is a bit of a misnomer since we need to jump through | 
|  | *         hoops to get a log record that parses ok yet does nothing. | 
|  | * | 
|  | *                                                  - dxm 29/09/00 | 
|  | */ | 
|  |  | 
|  | #include <config.h> | 
|  |  | 
|  | #include <xfs/libxfs.h> | 
|  | #ifdef HAVE_XFS_XFS_LOG_FORMAT_H | 
|  | #include <xfs/xfs_log_format.h> | 
|  | #define XFS_TRANS_MAGIC	XFS_TRANS_HEADER_MAGIC | 
|  | #else /* HAVE_XFS_XFS_LOG_FORMAT_H */ | 
|  | #include <xfs/xfs_log.h> | 
|  | #include <xfs/xfs_log_priv.h> | 
|  | #endif /* HAVE_XFS_XFS_LOG_FORMAT_H */ | 
|  |  | 
|  | #ifndef ASSIGN_ANY_LSN_DISK | 
|  | #define ASSIGN_ANY_LSN_DISK(lsn,cycle,block)  \ | 
|  | { \ | 
|  | INT_SET(((uint *)&(lsn))[0], ARCH_CONVERT, (cycle)); \ | 
|  | INT_SET(((uint *)&(lsn))[1], ARCH_CONVERT, (block)); \ | 
|  | } | 
|  | #endif | 
|  |  | 
|  | void | 
|  | usage(void) | 
|  | { | 
|  | fprintf(stderr,"Usage: loggen\n" | 
|  | "           set up parameters before writing record(s):\n" | 
|  | "               -f f     - set format\n" | 
|  | "               -u u     - set uuid\n" | 
|  | "               -c c     - set cycle\n" | 
|  | "               -b b     - set block\n" | 
|  | "               -C c     - set tail cycle\n" | 
|  | "               -B b     - set tail block\n" | 
|  | "           write log record(s):\n" | 
|  | "               -z n     - write n zero block(s)     (1BB)\n" | 
|  | "               -e n     - write n empty record(s)   (2BB)\n" | 
|  | "               -m n     - write n unmount record(s) (2BB)\n" | 
|  | "\n" | 
|  | "            redirect stdout to external log partition, or pipe to\n" | 
|  | "            dd with appropriate parameters to stuff into internal log.\n" | 
|  | ); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | int         bufblocks            = 0; | 
|  | void        *buf                 = NULL; | 
|  | int         param_cycle          = 1; | 
|  | int         param_block          = 0; | 
|  | int         param_tail_cycle     = 1; | 
|  | int         param_tail_block     = 0; | 
|  | int         param_fmt            = XLOG_FMT; | 
|  | uuid_t      param_uuid           = {0}; | 
|  |  | 
|  | void | 
|  | loggen_alloc(int blocks) | 
|  | { | 
|  | if (!(buf=realloc(buf, blocks*BBSIZE))) { | 
|  | fprintf(stderr,"failed to allocate %d block(s)\n", blocks); | 
|  | exit(1); | 
|  | } | 
|  | memset(buf, 0, blocks*BBSIZE); | 
|  | bufblocks=blocks; | 
|  | } | 
|  |  | 
|  | void | 
|  | loggen_write(void) | 
|  | { | 
|  | if (!buf) { | 
|  | fprintf(stderr,"no buffer allocated\n"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | if (fwrite(buf, BBSIZE, bufblocks, stdout) != bufblocks) { | 
|  | perror("fwrite"); | 
|  | exit(1); | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | void | 
|  | loggen_zero(int count) | 
|  | { | 
|  | if (!count) count=1; | 
|  |  | 
|  | fprintf(stderr,"   *** zero block (1BB) x %d\n", count); | 
|  | loggen_alloc(1); | 
|  | while (count--) | 
|  | loggen_write(); | 
|  | } | 
|  |  | 
|  | void | 
|  | loggen_unmount(int count) | 
|  | { | 
|  | xlog_rec_header_t       *head; | 
|  | xlog_op_header_t        *op; | 
|  | /* the data section must be 32 bit size aligned */ | 
|  | struct { | 
|  | uint16_t magic; | 
|  | uint16_t pad1; | 
|  | uint32_t pad2; /* may as well make it 64 bits */ | 
|  | } magic = { XLOG_UNMOUNT_TYPE, 0, 0 }; | 
|  |  | 
|  | if (!count) count=1; | 
|  |  | 
|  | fprintf(stderr,"   *** unmount record (2BB) x %d\n", count); | 
|  | loggen_alloc(2); | 
|  |  | 
|  | head = (xlog_rec_header_t *)buf; | 
|  | op   = (xlog_op_header_t  *)(((char*)buf)+BBSIZE); | 
|  |  | 
|  | /* note that oh_tid actually contains the cycle number | 
|  | * and the tid is stored in h_cycle_data[0] - that's the | 
|  | * way things end up on disk. | 
|  | */ | 
|  |  | 
|  | head->h_magicno = cpu_to_be32(XLOG_HEADER_MAGIC_NUM); | 
|  | head->h_cycle = cpu_to_be32(param_cycle); | 
|  | head->h_version = cpu_to_be32(1); | 
|  | head->h_len = cpu_to_be32(20); | 
|  | head->h_prev_block = cpu_to_be32(-1); | 
|  | head->h_num_logops = cpu_to_be32(1); | 
|  | head->h_cycle_data[0] = cpu_to_be32(0xb0c0d0d0); | 
|  | head->h_fmt = cpu_to_be32(param_fmt); | 
|  |  | 
|  | head->h_tail_lsn = cpu_to_be64(xlog_assign_lsn(param_tail_cycle, | 
|  | param_tail_block)); | 
|  |  | 
|  | memcpy(head->h_fs_uuid,  param_uuid, sizeof(uuid_t)); | 
|  |  | 
|  | /* now a log unmount op */ | 
|  | op->oh_tid = cpu_to_be32(param_cycle); | 
|  | op->oh_len = cpu_to_be32(sizeof(magic)); | 
|  | op->oh_clientid = XFS_LOG; | 
|  | op->oh_flags = XLOG_UNMOUNT_TRANS; | 
|  | op->oh_res2 = cpu_to_be16(0); | 
|  |  | 
|  | /* and the data for this op */ | 
|  |  | 
|  | memcpy(op+1, &magic, sizeof(magic)); | 
|  |  | 
|  | while (count--) { | 
|  | head->h_lsn = cpu_to_be64(xlog_assign_lsn(param_cycle, param_block++)); | 
|  |  | 
|  | loggen_write(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | loggen_empty(int count) | 
|  | { | 
|  | xlog_rec_header_t       *head; | 
|  | xlog_op_header_t        *op1, *op2, *op3, *op4, *op5; | 
|  | xfs_trans_header_t      *trans; | 
|  | xfs_buf_log_format_t    blfs; | 
|  | xfs_buf_log_format_t    *blf; | 
|  | int                     *data; | 
|  | char                    *p; | 
|  |  | 
|  | if (!count) count=1; | 
|  |  | 
|  | fprintf(stderr,"   *** empty record (2BB) x %d\n", count); | 
|  | loggen_alloc(2); | 
|  |  | 
|  | p=(char*)buf; | 
|  | head  = (xlog_rec_header_t   *)p;         p+=BBSIZE; | 
|  | op1   = (xlog_op_header_t    *)p;         p+=sizeof(xlog_op_header_t); | 
|  | op2   = (xlog_op_header_t    *)p;         p+=sizeof(xlog_op_header_t); | 
|  | trans = (xfs_trans_header_t  *)p;         p+=sizeof(xfs_trans_header_t); | 
|  | op3   = (xlog_op_header_t    *)p;         p+=sizeof(xlog_op_header_t); | 
|  | blf   = (xfs_buf_log_format_t*)p;         p+=sizeof(xfs_buf_log_format_t); | 
|  | op4   = (xlog_op_header_t    *)p;         p+=sizeof(xlog_op_header_t); | 
|  | data  = (int                 *)p;         p+=sizeof(int); | 
|  | op5   = (xlog_op_header_t    *)p;         p+=sizeof(xlog_op_header_t); | 
|  |  | 
|  | /* note that oh_tid actually contains the cycle number | 
|  | * and the tid is stored in h_cycle_data[0] - that's the | 
|  | * way things end up on disk. | 
|  | */ | 
|  |  | 
|  | head->h_magicno        = cpu_to_be32(XLOG_HEADER_MAGIC_NUM); | 
|  | head->h_cycle          = cpu_to_be32(param_cycle); | 
|  | head->h_version        = cpu_to_be32(1); | 
|  | head->h_len            = cpu_to_be32(5*sizeof(xlog_op_header_t) + | 
|  | sizeof(xfs_trans_header_t)+ | 
|  | sizeof(xfs_buf_log_format_t)+ | 
|  | sizeof(int)); | 
|  | head->h_prev_block     = cpu_to_be32(-1); | 
|  | head->h_num_logops     = cpu_to_be32(5); | 
|  | head->h_cycle_data[0]  = cpu_to_be32(0xb0c0d0d0); | 
|  | head->h_fmt            = cpu_to_be32(param_fmt); | 
|  |  | 
|  | head->h_tail_lsn = cpu_to_be64(xlog_assign_lsn(param_tail_cycle, | 
|  | param_tail_block)); | 
|  |  | 
|  | memcpy(head->h_fs_uuid, param_uuid, sizeof(uuid_t)); | 
|  |  | 
|  | /* start */ | 
|  | op1->oh_tid            = cpu_to_be32(1); | 
|  | op1->oh_len            = cpu_to_be32(0); | 
|  | op1->oh_clientid       = XFS_TRANSACTION; | 
|  | op1->oh_flags          = XLOG_START_TRANS; | 
|  | op1->oh_res2           = cpu_to_be16(0); | 
|  | /* dummy */ | 
|  | op2->oh_tid            = cpu_to_be32(0xb0c0d0d0); | 
|  | op2->oh_len            = cpu_to_be32(sizeof(xfs_trans_header_t)); | 
|  | op2->oh_clientid       = XFS_TRANSACTION; | 
|  | op2->oh_flags          = 0; | 
|  | op2->oh_res2           = cpu_to_be16(0); | 
|  | /* dummy transaction - this stuff doesn't get endian converted */ | 
|  | trans->th_magic     = XFS_TRANS_MAGIC; | 
|  | trans->th_type      = XFS_TRANS_DUMMY1; | 
|  | trans->th_tid       = 0; | 
|  | trans->th_num_items = 1; | 
|  | /* buffer */ | 
|  | op3->oh_tid            = cpu_to_be32(0xb0c0d0d0); | 
|  | op3->oh_len            = cpu_to_be32(sizeof(xfs_buf_log_format_t)); | 
|  | op3->oh_clientid       = XFS_TRANSACTION; | 
|  | op3->oh_flags          = 0; | 
|  | op3->oh_res2           = cpu_to_be16(0); | 
|  | /* an empty buffer too */ | 
|  | blfs.blf_type       = XFS_LI_BUF; | 
|  | blfs.blf_size       = 2; | 
|  | #ifdef XFS_BLF_CANCEL | 
|  | blfs.blf_flags      = XFS_BLF_CANCEL; | 
|  | #else | 
|  | blfs.blf_flags      = XFS_BLI_CANCEL; | 
|  | #endif | 
|  | blfs.blf_len        = 0; | 
|  | blfs.blf_blkno      = 1; | 
|  | blfs.blf_map_size   = 1; | 
|  | blfs.blf_data_map[0]= 0; | 
|  | memcpy(blf, &blfs, sizeof(blfs)); | 
|  | /* commit */ | 
|  | op4->oh_tid            = cpu_to_be32(0xb0c0d0d0); | 
|  | op4->oh_len            = cpu_to_be32(sizeof(int)); | 
|  | op4->oh_clientid       = XFS_TRANSACTION; | 
|  | op4->oh_flags          = 0; | 
|  | op4->oh_res2           = cpu_to_be16(0); | 
|  | /* and the data */ | 
|  | *data=*(int*)(char*)"FISH"; /* this won't get written (I hope) */ | 
|  | /* commit */ | 
|  | op5->oh_tid            = cpu_to_be32(0xb0c0d0d0); | 
|  | op5->oh_len            = cpu_to_be32(0); | 
|  | op5->oh_clientid       = XFS_TRANSACTION; | 
|  | op5->oh_flags          = XLOG_COMMIT_TRANS; | 
|  | op5->oh_res2           = cpu_to_be16(0); | 
|  |  | 
|  | while (count--) { | 
|  | head->h_lsn = cpu_to_be64(xlog_assign_lsn(param_cycle, param_block++)); | 
|  |  | 
|  | loggen_write(); | 
|  | } | 
|  | } | 
|  |  | 
|  | int | 
|  | main(int argc, char *argv[]) | 
|  | { | 
|  | int c; | 
|  |  | 
|  | fprintf(stderr,"*** loggen\n"); | 
|  |  | 
|  | if (argc<2) usage(); | 
|  |  | 
|  | while ((c = getopt(argc, argv, "f:u:c:b:C:B:z:e:m:")) != -1) { | 
|  | switch (c) { | 
|  | case 'f': | 
|  | param_fmt=atoi(optarg); | 
|  | break; | 
|  | case 'u': | 
|  | memset(param_uuid, atoi(optarg), sizeof(param_uuid)); | 
|  | break; | 
|  | case 'c': | 
|  | param_cycle=atoi(optarg); | 
|  | break; | 
|  | case 'b': | 
|  | param_block=atoi(optarg); | 
|  | break; | 
|  | case 'C': | 
|  | param_tail_cycle=atoi(optarg); | 
|  | break; | 
|  | case 'B': | 
|  | param_tail_block=atoi(optarg); | 
|  | break; | 
|  |  | 
|  | case 'z': | 
|  | loggen_zero(atoi(optarg)); | 
|  | break; | 
|  | case 'e': | 
|  | loggen_empty(atoi(optarg)); | 
|  | break; | 
|  | case 'm': | 
|  | loggen_unmount(atoi(optarg)); | 
|  | break; | 
|  |  | 
|  | default: | 
|  | fprintf(stderr, "unknown option\n"); | 
|  | usage(); | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } |