int btrfs_mkfs(cdico *d, char *partition, char *fsoptions) { char command[2048]; char buffer[2048]; char options[2048]; u64 compat_flags; u64 incompat_flags; u64 compat_ro_flags; int exitst; u64 temp64; // ---- get original filesystem features (if the original filesystem was a btrfs) if (dico_get_u64(d, 0, FSYSHEADKEY_BTRFSFEATURECOMPAT, &compat_flags)!=0 || dico_get_u64(d, 0, FSYSHEADKEY_BTRFSFEATUREINCOMPAT, &incompat_flags)!=0 || dico_get_u64(d, 0, FSYSHEADKEY_BTRFSFEATUREROCOMPAT, &compat_ro_flags)!=0) { // dont fail the original filesystem may not be a btrfs. in that case set defaults features compat_flags=0; incompat_flags=0; compat_ro_flags=0; } // ---- check there is no unsuported feature in that filesystem if (btrfs_check_compatibility(compat_flags, incompat_flags, compat_ro_flags)!=0) { errprintf("this filesystem has features which are not supported by this fsarchiver version.\n"); return -1; } // ---- there is no option that just displays the version and return 0 in mkfs.btrfs-0.16 if (exec_command(command, sizeof(command), NULL, NULL, 0, NULL, 0, "mkfs.btrfs")!=0) { errprintf("mkfs.btrfs not found. please install btrfs-progs on your system or check the PATH.\n"); return -1; } // ---- set the advanced filesystem settings from the dico memset(options, 0, sizeof(options)); strlcatf(options, sizeof(options), " %s ", fsoptions); if (dico_get_string(d, 0, FSYSHEADKEY_FSLABEL, buffer, sizeof(buffer))==0 && strlen(buffer)>0) strlcatf(options, sizeof(options), " -L '%s' ", buffer); if (dico_get_u64(d, 0, FSYSHEADKEY_FSBTRFSSECTORSIZE, &temp64)==0) strlcatf(options, sizeof(options), " -s %ld ", (long)temp64); if (exec_command(command, sizeof(command), &exitst, NULL, 0, NULL, 0, "mkfs.btrfs -f %s %s", partition, options)!=0 || exitst!=0) { errprintf("command [%s] failed\n", command); return -1; } return 0; }
int jfs_mkfs(cdico *d, char *partition, char *fsoptions, char *mkfslabel, char *mkfsuuid) { char command[2048]; char buffer[2048]; char options[2048]; int exitst; // ---- check mkfs.reiser4 is available if (exec_command(command, sizeof(command), NULL, NULL, 0, NULL, 0, "mkfs.jfs -V")!=0) { errprintf("mkfs.jfs not found. please install jfsutils on your system or check the PATH.\n"); return -1; } // ---- set the advanced filesystem settings from the dico memset(options, 0, sizeof(options)); strlcatf(options, sizeof(options), " %s ", fsoptions); if (strlen(mkfslabel) > 0) strlcatf(options, sizeof(options), " -L '%s' ", mkfslabel); else if (dico_get_string(d, 0, FSYSHEADKEY_FSLABEL, buffer, sizeof(buffer))==0 && strlen(buffer)>0) strlcatf(options, sizeof(options), " -L '%s' ", buffer); if (exec_command(command, sizeof(command), &exitst, NULL, 0, NULL, 0, "mkfs.jfs -q %s %s", options, partition)!=0 || exitst!=0) { errprintf("command [%s] failed\n", command); return -1; } // ---- use jfs_tune to set the other advanced options memset(options, 0, sizeof(options)); if (strlen(mkfsuuid) > 0) strlcatf(options, sizeof(options), " -U %s ", mkfsuuid); else if (dico_get_string(d, 0, FSYSHEADKEY_FSUUID, buffer, sizeof(buffer))==0 && strlen(buffer)==36) strlcatf(options, sizeof(options), " -U %s ", buffer); if (options[0]) { if (exec_command(command, sizeof(command), &exitst, NULL, 0, NULL, 0, "jfs_tune %s %s", partition, options)!=0 || exitst!=0) { errprintf("command [%s] failed\n", command); return -1; } } return 0; }
int fsaprintf(int level, bool showerrno, bool showloc, const char *file, const char *fct, int line, char *format, ...) { char buffer[8192]; char temp[1024]; bool msgscreen; bool msglogfile; va_list ap; // init memset(buffer, 0, sizeof(buffer)); msgscreen=(level <= g_options.verboselevel); msglogfile=(level <= g_options.debuglevel); if (msgscreen || msglogfile) { // 1. format errno and its meaning if (showerrno) strlcatf(buffer, sizeof(buffer), "[errno=%d, %s]: ", errno, strerror(errno)); // 2. format location of the message if (showloc) strlcatf(buffer, sizeof(buffer), "%s#%d,%s(): ", file, line, fct); // 3. format text message va_start(ap, format); vsnprintf(temp, sizeof(temp), format, ap); va_end(ap); strlcatf(buffer, sizeof(buffer), "%s", temp); // 4. show message on screen if (msgscreen) { fprintf(stderr, "%s", buffer); fflush(stderr); } // 5. write message in logfile if requested if (msglogfile) logfile_write(buffer, strlen(buffer)); } return 0; }
int dico_show(cdico *d, u8 section, char *debugtxt) { char buffer[2048]; char text[2048]; cdicoitem *item; assert(d); msgprintf(MSG_FORCE, "\n-----------------debug-dico-begin(%s)---------------\n", debugtxt); if (d->head) { for (item=d->head; item!=NULL; item=item->next) { if (item->section==section) { snprintf(buffer, sizeof(buffer), "key=[%ld], sizeof(data)=[%d], ", (long)item->key, (int)item->size); switch (item->type) { case DICTYPE_U8: snprintf(text, sizeof(text), "type=u8, size=[%d]", (int)item->size); break; case DICTYPE_U16: snprintf(text, sizeof(text), "type=u16, size=[%d]", (int)item->size); break; case DICTYPE_U32: snprintf(text, sizeof(text), "type=u32, size=[%d]", (int)item->size); break; case DICTYPE_U64: snprintf(text, sizeof(text), "type=u64, size=[%d]", (int)item->size); break; case DICTYPE_STRING: snprintf(text, sizeof(text), "type=str, size=[%d], data=[%s]", (int)item->size, (char*)item->data); break; case DICTYPE_DATA: snprintf(text, sizeof(text), "type=dat, size=[%d]", (int)item->size); break; default: snprintf(text, sizeof(text), "type=unknown"); break; } strlcatf(buffer, sizeof(buffer) ,"%s", text); msgprintf(MSG_FORCE, "%s\n", buffer); } } } else { msgprintf(MSG_FORCE, "dico is empty\n"); } msgprintf(MSG_FORCE, "-----------------debug-dico-end(%s)------------------\n\n", debugtxt); return 0; }
char *strlist_merge(cstrlist *l, char *bufdat, int bufsize, char sep) { cstrlistitem *item; if (!l || !bufdat || bufsize<=0) { errprintf("invalid param\n"); return NULL; } // init memset(bufdat, 0, bufsize); // item to remove not in first pos for (item=l->head; item!=NULL; item=item->next) { if (item!=l->head) // not the first item: write separator strlcatf(bufdat, bufsize, "%c", sep); strlcatf(bufdat, bufsize, "%s", item->str); } return bufdat; }
int serializer_dump(cserializer *a, char *buffer, int bufsize) { cserializeritem *cur; assert(a); assert(buffer); assert(bufsize>=0); memset(buffer, 0, bufsize); assert(pthread_mutex_lock(&a->mutex)==0); if (a->head==NULL) // empty { assert(pthread_mutex_unlock(&a->mutex)==0); return 1; } for (cur=a->head; cur!=NULL; cur=cur->next) strlcatf(buffer, bufsize, "val(%s,%s)=(%s)\n", cur->key1, cur->key2, cur->data); assert(pthread_mutex_unlock(&a->mutex)==0); return 0; }
int extfs_getinfo(cdico *d, char *devname) { struct fsa_ext2_sb *super; blk_t use_superblock=0; int use_blocksize=0; char uuid[512]; ext2_filsys fs; int origextfstype; char mntopt[1024]; char label[80]; u32 mask, m; int count; int i; // ---- open partition if (ext2fs_open(devname, EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_SOFTSUPP_FEATURES, use_superblock, use_blocksize, unix_io_manager, &fs)!=0) { errprintf("ext2fs_open(%s) failed\n", devname); return -1; } super=(struct fsa_ext2_sb *)fs->super; // --- label memset(label, 0, sizeof(label)); if (super->s_volume_name[0]) { memset(label, 0, sizeof(label)); strncpy(label, super->s_volume_name, sizeof(super->s_volume_name)); } dico_add_string(d, 0, FSYSHEADKEY_FSLABEL, label); // ---- uuid /*if ((str=e2p_uuid2str(super->s_uuid))!=NULL) dico_add_string(d, 0, FSYSHEADKEY_FSUUID, str);*/ memset(uuid, 0, sizeof(uuid)); uuid_unparse_lower((u8*)super->s_uuid, uuid); dico_add_string(d, 0, FSYSHEADKEY_FSUUID, uuid); msgprintf(MSG_DEBUG1, "extfs_uuid=[%s]\n", uuid); // ---- block size dico_add_u64(d, 0, FSYSHEADKEY_FSEXTBLOCKSIZE, EXT2_BLOCK_SIZE(super)); // ---- filesystem revision (good-old-rev or dynamic) dico_add_u64(d, 0, FSYSHEADKEY_FSEXTREVISION, super->s_rev_level); // ---- inode size if (super->s_rev_level >= EXT2_DYNAMIC_REV) dico_add_u64(d, 0, FSYSHEADKEY_FSINODESIZE, super->s_inode_size); else dico_add_u64(d, 0, FSYSHEADKEY_FSINODESIZE, EXT2_GOOD_OLD_INODE_SIZE); // Good old rev // ---- extended options if (super->s_raid_stride > 0) { dico_add_u64(d, 0, FSYSHEADKEY_FSEXTEOPTRAIDSTRIDE, super->s_raid_stride); msgprintf(MSG_DEBUG1, "extfs_raid_stride: %u\n", super->s_raid_stride); } if (super->s_raid_stripe_width > 0) { dico_add_u64(d, 0, FSYSHEADKEY_FSEXTEOPTRAIDSTRIPEWIDTH, super->s_raid_stripe_width); msgprintf(MSG_DEBUG1, "extfs_raid_stripe_width: %u\n", super->s_raid_stripe_width); } // ---- fsck details: max_mount_count and check_interval dico_add_u64(d, 0, FSYSHEADKEY_FSEXTFSCKMAXMNTCOUNT, max(super->s_max_mnt_count,0)); dico_add_u64(d, 0, FSYSHEADKEY_FSEXTFSCKCHECKINTERVAL, super->s_checkinterval); msgprintf(MSG_DEBUG1, "extfs_max_mount_count: %ld\n", (long)max(super->s_max_mnt_count,0)); msgprintf(MSG_DEBUG1, "extfs_check_interval: %ld\n", (long)super->s_checkinterval); // ---- default mount options memset(mntopt, 0, sizeof(mntopt)); count=0; mask=super->s_default_mount_opts; if (mask & EXT3_DEFM_JMODE) { strlcatf(mntopt, sizeof(mntopt), "%s", e2p_mntopt2string(mask & EXT3_DEFM_JMODE)); count++; } for (i=0, m=1; i < 32; i++, m<<=1) { if (m & EXT3_DEFM_JMODE) continue; if (mask & m) { if (count++) strlcatf(mntopt, sizeof(mntopt), ","); strlcatf(mntopt, sizeof(mntopt), "%s", e2p_mntopt2string(m)); } } dico_add_string(d, 0, FSYSHEADKEY_FSEXTDEFMNTOPT, mntopt); msgprintf(MSG_DEBUG1, "default mount options: [%s]\n", mntopt); // ---- filesystem features dico_add_u64(d, 0, FSYSHEADKEY_FSEXTFEATURECOMPAT, (u64)super->s_feature_compat); dico_add_u64(d, 0, FSYSHEADKEY_FSEXTFEATUREINCOMPAT, (u64)super->s_feature_incompat); dico_add_u64(d, 0, FSYSHEADKEY_FSEXTFEATUREROCOMPAT, (u64)super->s_feature_ro_compat); origextfstype=extfs_get_fstype_from_compat_flags((u64)super->s_feature_compat, (u64)super->s_feature_incompat, (u64)super->s_feature_ro_compat); msgprintf(MSG_DEBUG1, "the filesystem type determined by the features is [%s]\n", format_fstype(origextfstype)); // ---- check that fsarchiver is aware of all the filesystem features used on that filesystem if (extfs_check_compatibility((u64)super->s_feature_compat, (u64)super->s_feature_incompat, (u64)super->s_feature_ro_compat)!=0) { errprintf("this filesystem has ext{2,3,4} features which are not supported by this fsarchiver version.\n"); return -1; } // ---- minimum fsarchiver version required to restore dico_add_u64(d, 0, FSYSHEADKEY_MINFSAVERSION, FSA_VERSION_BUILD(0, 6, 4, 0)); ext2fs_close(fs); return 0; }
int extfs_mkfs(cdico *d, char *partition, int extfstype, char *fsoptions) { cstrlist strfeatures; u64 features_tab[3]; u64 fsextrevision; int origextfstype; char buffer[2048]; char command[2048]; char options[2048]; char temp[1024]; char progname[64]; u64 e2fstoolsver; int compat_type; u64 temp64; int exitst; int ret=0; int res; int i; // init memset(options, 0, sizeof(options)); snprintf(progname, sizeof(progname), "mke2fs"); strlist_init(&strfeatures); // ---- check that mkfs is installed and get its version if (exec_command(command, sizeof(command), NULL, NULL, 0, NULL, 0, "%s -V", progname)!=0) { errprintf("%s not found. please install a recent e2fsprogs on your system or check the PATH.\n", progname); ret=-1; goto extfs_mkfs_cleanup; } e2fstoolsver=check_prog_version(progname); // ---- filesystem revision (good-old-rev or dynamic) if (dico_get_u64(d, 0, FSYSHEADKEY_FSEXTREVISION, &fsextrevision)!=0) fsextrevision=EXT2_DYNAMIC_REV; // don't fail (case of fs conversion to extfs) // "mke2fs -q" prevents problems in exec_command when too many output details printed strlcatf(options, sizeof(options), " -q "); // filesystem revision: good-old-rev or dynamic strlcatf(options, sizeof(options), " -r %d ", (int)fsextrevision); strlcatf(options, sizeof(options), " %s ", fsoptions); // ---- set the advanced filesystem settings from the dico if (dico_get_string(d, 0, FSYSHEADKEY_FSLABEL, buffer, sizeof(buffer))==0 && strlen(buffer)>0) strlcatf(options, sizeof(options), " -L '%.16s' ", buffer); if (dico_get_u64(d, 0, FSYSHEADKEY_FSEXTBLOCKSIZE, &temp64)==0) strlcatf(options, sizeof(options), " -b %ld ", (long)temp64); if (dico_get_u64(d, 0, FSYSHEADKEY_FSINODESIZE, &temp64)==0) strlcatf(options, sizeof(options), " -I %ld ", (long)temp64); // ---- get original filesystem features (if the original filesystem was an ext{2,3,4}) if (dico_get_u64(d, 0, FSYSHEADKEY_FSEXTFEATURECOMPAT, &features_tab[E2P_FEATURE_COMPAT])!=0 || dico_get_u64(d, 0, FSYSHEADKEY_FSEXTFEATUREINCOMPAT, &features_tab[E2P_FEATURE_INCOMPAT])!=0 || dico_get_u64(d, 0, FSYSHEADKEY_FSEXTFEATUREROCOMPAT, &features_tab[E2P_FEATURE_RO_INCOMPAT])!=0) { // dont fail the original filesystem may not be ext{2,3,4}. in that case set defaults features features_tab[E2P_FEATURE_COMPAT]=EXT2_FEATURE_COMPAT_RESIZE_INODE|EXT2_FEATURE_COMPAT_DIR_INDEX; features_tab[E2P_FEATURE_INCOMPAT]=EXT2_FEATURE_INCOMPAT_FILETYPE; features_tab[E2P_FEATURE_RO_INCOMPAT]=EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; } // ---- check that fsarchiver is aware of all the filesystem features used on that filesystem if (extfs_check_compatibility(features_tab[E2P_FEATURE_COMPAT], features_tab[E2P_FEATURE_INCOMPAT], features_tab[E2P_FEATURE_RO_INCOMPAT])!=0) { errprintf("this filesystem has ext{2,3,4} features which are not supported by this fsarchiver version.\n"); return -1; } // ---- get original filesystem type origextfstype=extfs_get_fstype_from_compat_flags(features_tab[E2P_FEATURE_COMPAT], features_tab[E2P_FEATURE_INCOMPAT], features_tab[E2P_FEATURE_RO_INCOMPAT]); msgprintf(MSG_VERB2, "the filesystem type determined by the original filesystem features is [%s]\n", format_fstype(origextfstype)); // remove all the features not supported by the filesystem to create (conversion = downgrade fs) for (i=0; mkfeatures[i].name; i++) { compat_type=mkfeatures[i].compat; if (mkfeatures[i].firstfs > extfstype) features_tab[compat_type] &= ~mkfeatures[i].mask; } // add new features if the filesystem to create is newer than the filesystem type that was backed up // eg: user did a "savefs" of an ext3 and does a "restfs mkfs=ext4" --> add features to force ext4 // it's a bit more difficult because we only want to add such a feature if no feature of the new // filesystem is currently enabled. msgprintf(MSG_VERB2, "the filesystem type to create considering the command options is [%s]\n", format_fstype(extfstype)); if (origextfstype==EXTFSTYPE_EXT2 && extfstype>EXTFSTYPE_EXT2) // upgrade ext2 to ext{3,4} { fsextrevision=EXT2_DYNAMIC_REV; features_tab[E2P_FEATURE_COMPAT]|=EXT3_FEATURE_COMPAT_HAS_JOURNAL; } if (origextfstype<EXTFSTYPE_EXT4 && extfstype>=EXTFSTYPE_EXT4) // upgrade ext{2,3} to ext4 { fsextrevision=EXT2_DYNAMIC_REV; features_tab[E2P_FEATURE_INCOMPAT]|=EXT3_FEATURE_INCOMPAT_EXTENTS; } // convert int features to string to be passed to mkfs for (i=0; mkfeatures[i].name; i++) { if (mkfeatures[i].firste2p<=e2fstoolsver) // don't pass an option to a program that does not support it { compat_type=mkfeatures[i].compat; if (features_tab[compat_type] & mkfeatures[i].mask) { msgprintf(MSG_VERB2, "--> feature [%s]=YES\n", mkfeatures[i].name); strlist_add(&strfeatures, mkfeatures[i].name); } else { msgprintf(MSG_VERB2, "--> feature [%s]=NO\n", mkfeatures[i].name); snprintf(temp, sizeof(temp), "^%s", mkfeatures[i].name); // exclude feature strlist_add(&strfeatures, temp); } } } // if extfs revision is dynamic and there are features in the list if (fsextrevision!=EXT2_GOOD_OLD_REV && strlist_count(&strfeatures)>0) { strlist_merge(&strfeatures, temp, sizeof(temp), ','); strlcatf(options, sizeof(options), " -O %s ", temp); msgprintf(MSG_VERB2, "features: mkfs_options+=[-O %s]\n", temp); } // ---- check mke2fs version requirement msgprintf(MSG_VERB2, "mke2fs version detected: %s\n", format_prog_version(e2fstoolsver, temp, sizeof(temp))); msgprintf(MSG_VERB2, "mke2fs version required: %s\n", format_prog_version(e2fsprogs_minver[extfstype], temp, sizeof(temp))); if (e2fstoolsver < e2fsprogs_minver[extfstype]) { errprintf("mke2fs was found but is too old, please upgrade to a version %s or more recent.\n", format_prog_version(e2fsprogs_minver[extfstype], temp, sizeof(temp))); ret=-1; goto extfs_mkfs_cleanup; } // ---- extended options if (dico_get_u64(d, 0, FSYSHEADKEY_FSEXTEOPTRAIDSTRIDE, &temp64)==0) strlcatf(options, sizeof(options), " -E stride=%ld ", (long)temp64); if ((dico_get_u64(d, 0, FSYSHEADKEY_FSEXTEOPTRAIDSTRIPEWIDTH, &temp64)==0) && e2fstoolsver>=PROGVER(1,40,7)) strlcatf(options, sizeof(options), " -E stripe-width=%ld ", (long)temp64); // ---- execute mke2fs msgprintf(MSG_VERB2, "exec: %s\n", command); if (exec_command(command, sizeof(command), &exitst, NULL, 0, NULL, 0, "%s %s %s", progname, partition, options)!=0 || exitst!=0) { errprintf("command [%s] failed with return status=%d\n", command, exitst); ret=-1; goto extfs_mkfs_cleanup; } // ---- use tune2fs to set the other advanced options memset(options, 0, sizeof(options)); if (dico_get_string(d, 0, FSYSHEADKEY_FSUUID, buffer, sizeof(buffer))==0 && strlen(buffer)==36) strlcatf(options, sizeof(options), " -U %s ", buffer); if (dico_get_string(d, 0, FSYSHEADKEY_FSEXTDEFMNTOPT, buffer, sizeof(buffer))==0 && strlen(buffer)>0) strlcatf(options, sizeof(options), " -o %s ", buffer); if (dico_get_u64(d, 0, FSYSHEADKEY_FSEXTFSCKMAXMNTCOUNT, &temp64)==0) strlcatf(options, sizeof(options), " -c %ld ", (long)temp64); if (dico_get_u64(d, 0, FSYSHEADKEY_FSEXTFSCKCHECKINTERVAL, &temp64)==0) strlcatf(options, sizeof(options), " -i %ldd ", (long)(temp64/86400L)); if (options[0]) { if (exec_command(command, sizeof(command), &exitst, NULL, 0, NULL, 0, "tune2fs %s %s", partition, options)!=0 || exitst!=0) { errprintf("command [%s] failed with return status=%d\n", command, exitst); ret=-1; goto extfs_mkfs_cleanup; } // run e2fsck to workaround an tune2fs bug in e2fsprogs < 1.41.4 on ext4 // http://article.gmane.org/gmane.comp.file-systems.ext4/11181 if (extfstype==EXTFSTYPE_EXT4 && e2fstoolsver<PROGVER(1,41,4)) { if ( ((res=exec_command(command, sizeof(command), &exitst, NULL, 0, NULL, 0, "e2fsck -fy %s", partition))!=0) || ((exitst!=0) && (exitst!=1)) ) { errprintf("command [%s] failed with return status=%d\n", command, exitst); ret=-1; goto extfs_mkfs_cleanup; } } } extfs_mkfs_cleanup: strlist_destroy(&strfeatures); return ret; }
int xfs_mkfs(cdico *d, char *partition, char *fsoptions, char *mkfslabel, char *mkfsuuid) { char stdoutbuf[2048]; char command[2048]; char buffer[2048]; char mkfsopts[2048]; char xadmopts[2048]; char uuid[64]; u64 xfstoolsver; int exitst; u64 temp64; u64 xfsver; int x, y, z; int optval; u64 sb_features_compat=0; u64 sb_features_ro_compat=0; u64 sb_features_incompat=0; u64 sb_features_log_incompat=0; // ---- check that mkfs is installed and get its version if (exec_command(command, sizeof(command), NULL, stdoutbuf, sizeof(stdoutbuf), NULL, 0, "mkfs.xfs -V")!=0) { errprintf("mkfs.xfs not found. please install xfsprogs on your system or check the PATH.\n"); return -1; } x=y=z=0; sscanf(stdoutbuf, "mkfs.xfs version %d.%d.%d", &x, &y, &z); if (x==0 && y==0) { errprintf("Can't parse mkfs.xfs version number: x=y=0\n"); return -1; } xfstoolsver=PROGVER(x,y,z); msgprintf(MSG_VERB2, "Detected mkfs.xfs version %d.%d.%d\n", x, y, z); memset(mkfsopts, 0, sizeof(mkfsopts)); memset(xadmopts, 0, sizeof(xadmopts)); memset(uuid, 0, sizeof(uuid)); strlcatf(mkfsopts, sizeof(mkfsopts), " %s ", fsoptions); if (strlen(mkfslabel) > 0) strlcatf(mkfsopts, sizeof(mkfsopts), " -L '%.12s' ", mkfslabel); else if (dico_get_string(d, 0, FSYSHEADKEY_FSLABEL, buffer, sizeof(buffer))==0 && strlen(buffer)>0) strlcatf(mkfsopts, sizeof(mkfsopts), " -L '%.12s' ", buffer); if ((dico_get_u64(d, 0, FSYSHEADKEY_FSXFSBLOCKSIZE, &temp64)==0) && (temp64%512==0) && (temp64>=512) && (temp64<=65536)) strlcatf(mkfsopts, sizeof(mkfsopts), " -b size=%ld ", (long)temp64); // ---- get xfs features attributes from the archive dico_get_u64(d, 0, FSYSHEADKEY_FSXFSFEATURECOMPAT, &sb_features_compat); dico_get_u64(d, 0, FSYSHEADKEY_FSXFSFEATUREROCOMPAT, &sb_features_ro_compat); dico_get_u64(d, 0, FSYSHEADKEY_FSXFSFEATUREINCOMPAT, &sb_features_incompat); dico_get_u64(d, 0, FSYSHEADKEY_FSXFSFEATURELOGINCOMPAT, &sb_features_log_incompat); // ---- check fsarchiver is aware of all the filesystem features used on that filesystem if (xfs_check_compatibility(sb_features_compat, sb_features_ro_compat, sb_features_incompat, sb_features_log_incompat)!=0) return -1; // Preserve version 4 of XFS if the original filesystem was an XFSv4 or if // it was saved with fsarchiver <= 0.6.19 which does not store the XFS // version in the metadata: make an XFSv4 if unsure for better compatibility, // as upgrading later is easier than downgrading if ((dico_get_u64(d, 0, FSYSHEADKEY_FSXFSVERSION, &temp64)!=0) || (temp64==XFS_SB_VERSION_4)) xfsver = XFS_SB_VERSION_4; else xfsver = XFS_SB_VERSION_5; // Unfortunately it is impossible to preserve the UUID (stamped on every // metadata block) of an XFSv5 filesystem with mkfs.xfs < 4.3.0. // Restoring with a new random UUID would work but could prevent the system // from booting if this is a boot/root filesystem because grub/fstab often // use the UUID to identify it. Hence it is much safer to restore it as an // XFSv4 and it also provides a better compatibility with older kernels. // Hence this is the safest option. // More details: https://github.com/fdupoux/fsarchiver/issues/4 if ((xfsver==XFS_SB_VERSION_5) && (xfstoolsver < PROGVER(4,3,0))) { xfsver = XFS_SB_VERSION_4; // Do not preserve the XFS version msgprintf(MSG_FORCE, "It is impossible to restore this filesystem as an XFSv5 and preserve its UUID\n" "with mkfs.xfs < 4.3.0. This filesystem will be restored as an XFSv4 instead\n" "as this is a much safer option (preserving the UUID may be required on\n" "boot/root filesystems for the operating system to be able to start). If you\n" "really want to have an XFSv5 filesystem, please upgrade xfsprogs to version\n" "4.3.0 or more recent and rerun this operation to get an XFSv5 with the same\n" "UUID as original filesystem\n"); } // Determine if the "crc" mkfs option should be enabled (checksum) // - checksum must be disabled if we want to recreate an XFSv4 filesystem // - checksum must be enabled if we want to recreate an XFSv5 filesystem if (xfstoolsver >= PROGVER(3,2,0)) // only use "crc" option when it is supported by mkfs { optval = (xfsver==XFS_SB_VERSION_5); strlcatf(mkfsopts, sizeof(mkfsopts), " -m crc=%d ", (int)optval); } // Determine if the "finobt" mkfs option should be enabled (free inode btree) // - starting with linux-3.16 XFS has added a btree that tracks free inodes // - this feature relies on the new v5 on-disk format but it is optional // - this feature is enabled by default when using xfsprogs 3.2.3 or later // - this feature will be enabled if the original filesystem was XFSv5 and had it if (xfstoolsver >= PROGVER(3,2,1)) // only use "finobt" option when it is supported by mkfs { optval = ((xfsver==XFS_SB_VERSION_5) && (sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_FINOBT)); strlcatf(mkfsopts, sizeof(mkfsopts), " -m finobt=%d ", (int)optval); } // Determine if the "rmapbt" mkfs option should be enabled (reverse mapping btree) // - starting with linux-4.8 XFS has added a btree that maps filesystem blocks // to their owner // - this feature relies on the new v5 on-disk format but it is optional // - this feature will be enabled if the original filesystem was XFSv5 and had it if (xfstoolsver >= PROGVER(4,8,0)) // only use "rmapbt" option when it is supported by mkfs { optval = ((xfsver==XFS_SB_VERSION_5) && (sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_RMAPBT)); strlcatf(mkfsopts, sizeof(mkfsopts), " -m rmapbt=%d ", (int)optval); } // Determine if the "reflink" mkfs option should be enabled // - starting with linux-4.9 XFS has added support for reflinked files // - this feature relies on the new v5 on-disk format but it is optional // - this feature will be enabled if the original filesystem was XFSv5 and had it if (xfstoolsver >= PROGVER(4,9,0)) // only use "reflink" option when it is supported by mkfs { optval = ((xfsver==XFS_SB_VERSION_5) && (sb_features_ro_compat & XFS_SB_FEAT_RO_COMPAT_REFLINK)); strlcatf(mkfsopts, sizeof(mkfsopts), " -m reflink=%d ", (int)optval); } // Attempt to preserve UUID of the filesystem // - the "-m uuid=<UUID>" option in mkfs.xfs was added in mkfs.xfs 4.3.0 and is the best way to set UUIDs // - the UUID of XFSv4 can be successfully set using either xfs_admin or mkfs.xfs >= 4.3.0 // - it is impossible to set both types of UUIDs of an XFSv5 filesystem using xfsprogs < 4.3.0 // for this reason the XFS version is forced to v4 if xfsprogs version < 4.3.0 if (strlen(mkfsuuid) > 0) snprintf(uuid, sizeof(uuid), "%s", mkfsuuid); else if (dico_get_string(d, 0, FSYSHEADKEY_FSUUID, buffer, sizeof(buffer))==0) snprintf(uuid, sizeof(uuid), "%s", buffer); if (strlen(uuid)==36) { if (xfstoolsver >= PROGVER(4,3,0)) strlcatf(mkfsopts, sizeof(mkfsopts), " -m uuid=%s ", uuid); else strlcatf(xadmopts, sizeof(xadmopts), " -U %s ", uuid); } // Determine if the "ftype" mkfs option should be enabled (filetype in dirent) // - this feature allows the inode type to be stored in the directory structure // - mkfs.xfs 4.2.0 enabled ftype by default (supported since mkfs.xfs 3.2.0) for XFSv4 volumes // - when CRCs are enabled via -m crc=1, the ftype functionality is always enabled // - ftype is madatory in XFSv5 volumes but it is optional for XFSv4 volumes // - the "ftype" option must be specified after the "crc" option in mkfs.xfs < 4.2.0: // https://git.kernel.org/cgit/fs/xfs/xfsprogs-dev.git/commit/?id=b990de8ba4e2df2bc76a140799d3ddb4a0eac4ce // - do not set ftype=1 with crc=1 as mkfs.xfs may fail when both options are enabled (at least with xfsprogs-3.2.2) // - XFSv4 with ftype=1 is supported since linux-3.13. We purposely always // disable ftype for V4 volumes to keep them compatible with older kernels if (xfstoolsver >= PROGVER(3,2,0)) // only use "ftype" option when it is supported by mkfs { // ftype is already set to 1 when it is XFSv5 if (xfsver==XFS_SB_VERSION_4) strlcatf(mkfsopts, sizeof(mkfsopts), " -n ftype=0 "); } // Determine if the "sparse" mkfs option should be enabled (sparse inode allocation) // - starting with linux-4.2 XFS can allocate discontinuous inode chunks // - this feature relies on the new v5 on-disk format but it is optional // - this feature will be enabled if the original filesystem was XFSv5 and had it // - this feature is supported since mkfs.xfs 4.2.0, but unfortunately // mkfs.xfs 4.5.0 in RHEL 7.3 carries a custom patch removing the option, // hence require mkfs.xfs 4.7.0, next version after 4.5.0 if (xfstoolsver >= PROGVER(4,7,0)) // only use "sparse" option when it is supported by mkfs { optval = ((xfsver==XFS_SB_VERSION_5) && (sb_features_incompat & XFS_SB_FEAT_INCOMPAT_SPINODES)); strlcatf(mkfsopts, sizeof(mkfsopts), " -i sparse=%d ", (int)optval); } // ---- create the new filesystem using mkfs.xfs if (exec_command(command, sizeof(command), &exitst, NULL, 0, NULL, 0, "mkfs.xfs -f %s %s", partition, mkfsopts)!=0 || exitst!=0) { errprintf("command [%s] failed\n", command); return -1; } // ---- use xfs_admin to set the UUID if not already done with mkfs.xfs if (xadmopts[0]) { if (exec_command(command, sizeof(command), &exitst, NULL, 0, NULL, 0, "xfs_admin %s %s", xadmopts, partition)!=0 || exitst!=-0) { errprintf("command [%s] failed\n", command); return -1; } } return 0; }