[syslinux:pathbased] pathbased:btrfs: initial subvol support

syslinux-bot for Alek Du alek.du at intel.com
Wed Jan 13 15:48:08 PST 2010


Commit-ID:  18f397e4f6d854798a967841c452adc1d8b3d1fc
Gitweb:     http://syslinux.zytor.com/commit/18f397e4f6d854798a967841c452adc1d8b3d1fc
Author:     Alek Du <alek.du at intel.com>
AuthorDate: Wed, 13 Jan 2010 15:06:15 +0800
Committer:  H. Peter Anvin <hpa at zytor.com>
CommitDate: Wed, 13 Jan 2010 15:30:10 -0800

pathbased:btrfs: initial subvol support

Added "Subvol" name in the extlinux.sys, and then btrfs fs code will
handle the subvol correctly. Also fixed the bug where CurrentDirName
and SubvolName should not exist in the first sector.


---
 core/diskstart.inc      |   21 +++++-----
 core/fs/btrfs/btrfs.c   |  100 +++++++++++++++++++++++++++++++++++++++++-----
 core/fs/btrfs/btrfs.h   |    7 +++
 core/include/core.h     |    1 +
 extlinux/main.c         |   52 +++++++++++++++++++++---
 libinstaller/syslxint.h |    2 +
 6 files changed, 156 insertions(+), 27 deletions(-)

diff --git a/core/diskstart.inc b/core/diskstart.inc
index 3bffc89..ca627b1 100644
--- a/core/diskstart.inc
+++ b/core/diskstart.inc
@@ -494,19 +494,11 @@ CheckSum	dd 0		; Checksum starting at ldlinux_sys
 				; value = LDLINUX_MAGIC - [sum of dwords]
 CurrentDirPtr	dw CurrentDirName-LDLINUX_SYS	; Current directory name string
 CurrentDirLen	dw FILENAME_MAX
+SubvolPtr	dw SubvolName - LDLINUX_SYS
+SubvolLen	dw 64		; Should be enough
 SecPtrOffset	dw SectorPtrs - LDLINUX_SYS
 SecPtrCnt	dw (SectorPtrsEnd - SectorPtrs) >> 2
 
-;
-; Installer pokes the base directory here, needs to be in .text16 so the pointer
-; address can be calculated by the assembler.
-;
-%define HAVE_CURRENTDIRNAME
-		section .data16
-		global CurrentDirName
-CurrentDirName	times FILENAME_MAX db 0
-
-		section .text16
 ldlinux_ent:
 ;
 ; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
@@ -699,6 +691,15 @@ SectorPtrsEnd	equ $
 ;  End of code and data that have to be in the first sector
 ; ----------------------------------------------------------------------------
 
+;
+; Installer pokes the base directory here, needs to be in .text16 so the pointer
+; address can be calculated by the assembler.
+;
+%define HAVE_CURRENTDIRNAME
+		section .data16
+		global CurrentDirName, SubvolName
+CurrentDirName	times FILENAME_MAX db 0
+SubvolName	times 64 db 0
 		section .text16
 all_read:
 ;
diff --git a/core/fs/btrfs/btrfs.c b/core/fs/btrfs/btrfs.c
index 31e24f7..ee04260 100644
--- a/core/fs/btrfs/btrfs.c
+++ b/core/fs/btrfs/btrfs.c
@@ -266,6 +266,21 @@ static int btrfs_comp_keys(struct btrfs_disk_key *k1, struct btrfs_disk_key *k2)
 	return 0;
 }
 
+/* compare keys but ignore offset, is useful to enumerate all same kind keys */
+static int btrfs_comp_keys_type(struct btrfs_disk_key *k1,
+					struct btrfs_disk_key *k2)
+{
+	if (k1->objectid > k2->objectid)
+		return 1;
+	if (k1->objectid < k2->objectid)
+		return -1;
+	if (k1->type > k2->type)
+		return 1;
+	if (k1->type < k2->type)
+		return -1;
+	return 0;
+}
+
 /* seach tree directly on disk ... */
 static int search_tree(u64 loffset, struct btrfs_disk_key *key,
 				struct btrfs_path *path)
@@ -394,8 +409,8 @@ static void btrfs_read_chunk_tree(void)
 		search_tree(sb.chunk_root, &search_key, &path);
 		do {
 			do {
-				if (path.item.key.objectid !=
-					BTRFS_FIRST_CHUNK_TREE_OBJECTID)
+				if (btrfs_comp_keys_type(&search_key,
+							&path.item.key))
 					break;
 				chunk = (struct btrfs_chunk *)(path.data);
 				/* insert to mapping table, ignore stripes */
@@ -405,8 +420,7 @@ static void btrfs_read_chunk_tree(void)
 				item.physical = chunk->stripe.offset;
 				insert_map(&item);
 			} while (!next_slot(&search_key, &path));
-			if (path.item.key.objectid !=
-				BTRFS_FIRST_CHUNK_TREE_OBJECTID)
+			if (btrfs_comp_keys_type(&search_key, &path.item.key))
 				break;
 		} while (!next_leaf(&search_key, &path));
 	}
@@ -417,13 +431,16 @@ static inline u64 btrfs_name_hash(const char *name, int len)
 	return btrfs_crc32c((u32)~1, name, len);
 }
 
-/* search a file with full path in fs_tree, do not support ../ ./ style path */
-static int btrfs_search_fs_tree(const char *fullpath, u64 *offset,
+/* search a file with full path or relative path */
+static int btrfs_search_fs_tree(const char *fpath, u64 *offset,
 						u64 *size, u8 *type)
 {
 	char name[256];
 	char *tmp = name;
-	u64 objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
+	/* this can be used as cwd during the file searching */
+	static u64 objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
+	static u8 level; /* dir level */
+	static u64 objs[16]; /* history for ../ operation */
 	int ret;
 	struct btrfs_disk_key search_key;
 	struct btrfs_path path;
@@ -431,16 +448,26 @@ static int btrfs_search_fs_tree(const char *fullpath, u64 *offset,
 	struct btrfs_inode_item inode_item;
 	struct btrfs_file_extent_item extent_item;
 
+	if (*fpath == '/' || *fpath == '\0') { /* full or null path */
+		objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
+		level = 0;
+		objs[level] = objectid;
+	}
 	*tmp = '\0';
 	while (1) {
-		char c = *(fullpath++);
+		char c = *(fpath++);
 
 		*(tmp++) = c;
 		if (!c)
 			break;
 		if (c == '/') {
 			*(tmp-1) = '\0';
-			if (strlen(name)) {/* a "real" dir */
+			if (!strcmp(name, "..")) {
+				if (level)
+					level--;
+				objectid = objs[level];
+			} else if (strlen(name) && strcmp(name, ".")) {
+				/* a "real" dir */
 				search_key.objectid = objectid;
 				search_key.type = BTRFS_DIR_ITEM_KEY;
 				search_key.offset =
@@ -456,6 +483,12 @@ static int btrfs_search_fs_tree(const char *fullpath, u64 *offset,
 					return -1;
 				}
 				objectid = dir_item.location.objectid;
+				level++;
+				if (level >= 16) {
+					printf("too many dir levels(>16)\n");
+					return -1;
+				}
+				objs[level] = objectid;
 			}
 			tmp = name;
 			*tmp = '\0';
@@ -531,6 +564,16 @@ static void btrfs_close_file(struct file *file)
     close_pvt(file->open_file);
 }
 
+/* set the cwd to the global Path in patcharea */
+static void btrfs_set_cwd(void)
+{
+	u64 tmp;
+	char path[FILENAME_MAX];
+
+	sprintf(path, "%s/", CurrentDirName);
+	btrfs_search_fs_tree(path, &tmp, &tmp, (u8 *)&tmp);
+}
+
 static void btrfs_searchdir(char *filename, struct file *file)
 {
 	struct open_file_t *open_file;
@@ -563,6 +606,8 @@ static void btrfs_searchdir(char *filename, struct file *file)
 		}
 		break;
 	} while (1);
+	/* restore the cwd, since the above search may change dir */
+	btrfs_set_cwd();
 }
 
 /* Load the config file, return 1 if failed, or 0 */
@@ -572,7 +617,6 @@ static int btrfs_load_config(void)
     com32sys_t regs;
 
     strcpy(ConfigName, config_name);
-    *(uint16_t *)CurrentDirName = ROOT_DIR_WORD;
 
     memset(&regs, 0, sizeof regs);
     regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
@@ -615,9 +659,42 @@ static void btrfs_get_fs_tree(void)
 	struct btrfs_disk_key search_key;
 	struct btrfs_path path;
 	struct btrfs_root_item *tree;
+	bool subvol_ok = false;
+
+	/* check if subvol is filled by installer */
+	if (*SubvolName) {
+		search_key.objectid = BTRFS_FS_TREE_OBJECTID;
+		search_key.type = BTRFS_ROOT_REF_KEY;
+		search_key.offset = 0;
+		clear_path(&path);
+		if (search_tree(sb.root, &search_key, &path))
+			next_slot(&search_key, &path);
+		do {
+			do {
+				struct btrfs_root_ref *ref;
 
+				if (btrfs_comp_keys_type(&search_key,
+							&path.item.key))
+					break;
+				ref = (struct btrfs_root_ref *)path.data;
+				if (!strcmp((char*)(ref + 1), SubvolName)) {
+					subvol_ok = true;
+					break;
+				}
+			} while (!next_slot(&search_key, &path));
+			if (subvol_ok)
+				break;
+			if (btrfs_comp_keys_type(&search_key, &path.item.key))
+				break;
+		} while (!next_leaf(&search_key, &path));
+		if (!subvol_ok) /* should be impossible */
+			printf("no subvol found!\n");
+	}
 	/* find fs_tree from tree_root */
-	search_key.objectid = BTRFS_FS_TREE_OBJECTID;
+	if (subvol_ok)
+		search_key.objectid = path.item.key.offset;
+	else /* "default" volume */
+		search_key.objectid = BTRFS_FS_TREE_OBJECTID;
 	search_key.type = BTRFS_ROOT_ITEM_KEY;
 	search_key.offset = -1;
 	clear_path(&path);
@@ -638,6 +715,7 @@ static int btrfs_fs_init(struct fs_info *_fs)
 	btrfs_read_sys_chunk_array();
 	btrfs_read_chunk_tree();
 	btrfs_get_fs_tree();
+	btrfs_set_cwd();
 	cache_ready = 1;
 	return BTRFS_BLOCK_SHIFT;/* to determine cache size */
 }
diff --git a/core/fs/btrfs/btrfs.h b/core/fs/btrfs/btrfs.h
index 8ec8afe..eacdeb5 100644
--- a/core/fs/btrfs/btrfs.h
+++ b/core/fs/btrfs/btrfs.h
@@ -35,6 +35,7 @@ typedef u64 __le64;
 
 #define BTRFS_DEV_ITEM_KEY	216
 #define BTRFS_CHUNK_ITEM_KEY	228
+#define BTRFS_ROOT_REF_KEY	156
 #define BTRFS_ROOT_ITEM_KEY	132
 #define BTRFS_EXTENT_DATA_KEY	108
 #define BTRFS_DIR_ITEM_KEY	84
@@ -272,4 +273,10 @@ struct btrfs_file_extent_item {
 	__le64 num_bytes;
 } __attribute__ ((__packed__));
 
+struct btrfs_root_ref {
+	__le64 dirid;
+	__le64 sequence;
+	__le16 name_len;
+} __attribute__ ((__packed__));
+
 #endif
diff --git a/core/include/core.h b/core/include/core.h
index 1a9e1b9..f54fcf8 100644
--- a/core/include/core.h
+++ b/core/include/core.h
@@ -8,6 +8,7 @@ extern char core_xfer_buf[65536];
 extern char core_cache_buf[65536];
 extern char trackbuf[];
 extern char CurrentDirName[];
+extern char SubvolName[];
 extern char ConfigName[];
 extern char KernelName[];
 
diff --git a/extlinux/main.c b/extlinux/main.c
index 05b390f..c29e3b0 100644
--- a/extlinux/main.c
+++ b/extlinux/main.c
@@ -139,6 +139,8 @@ static const char short_options[] = "iUuzS:H:rvho:O";
 /* the btrfs partition first 64K blank area is used to store boot sector and
    boot image, the boot sector is from 0~512, the boot image starts at 2K */
 #define BTRFS_EXTLINUX_OFFSET (2*1024)
+#define BTRFS_SUBVOL_OPT "subvol="
+static char subvol[64];
 /*
  * Boot block
  */
@@ -366,7 +368,7 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
     struct patch_area *patcharea;
     int i, dw, nptrs;
     uint32_t csum;
-    int secptroffset, diroffset, dirlen;
+    int secptroffset, diroffset, dirlen, subvoloffset, subvollen;
     char *dirpath, *subpath;
 
     dirpath = realpath(dir, NULL);
@@ -487,6 +489,16 @@ int patch_file_and_bootblock(int fd, const char *dir, int devfd)
     }
     strncpy((char *)boot_image + diroffset, subpath, dirlen);
     free(dirpath);
+    /* write subvol info if we have */
+    if (*subvol) {
+	subvoloffset = get_16(&patcharea->subvoloffset);
+	subvollen = get_16(&patcharea->subvollen);
+	if (subvollen <= strlen(subvol)) {
+	fprintf(stderr, "Subvol name too long... aborting install!\n");
+	exit(1);
+	}
+	strncpy((char *)boot_image + subvoloffset, subvol, subvollen);
+    }
 
     /* Now produce a checksum */
     set_32(&patcharea->checksum, 0);
@@ -888,8 +900,22 @@ static const char *find_device(const char *mtab_file, dev_t dev)
 	case BTRFS:
 		if (!strcmp(mnt->mnt_type, "btrfs") &&
 		    !stat(mnt->mnt_dir, &dst) &&
-		    dst.st_dev == dev)
-		    done = true;
+		    dst.st_dev == dev) {
+		    char *opt = strstr(mnt->mnt_opts, BTRFS_SUBVOL_OPT);
+
+		    if (opt) {
+			if (!subvol[0]) {
+			    char *tmp;
+
+			    strcpy(subvol, opt + sizeof(BTRFS_SUBVOL_OPT) - 1);
+			    tmp = strchr(subvol, 32);
+			    if (tmp)
+				*tmp = '\0';
+			}
+			break; /* should break and let upper layer try again */
+		    } else
+			done = true;
+		}
 		break;
 	case EXT2:
 		if ((!strcmp(mnt->mnt_type, "ext2") ||
@@ -943,10 +969,24 @@ static const char *get_devname(const char *path)
 
 #else
 
-    devname = find_device("/proc/mounts", st.st_dev);
+    /* check /etc/mtab first, since btrfs subvol info is only in here */
+    devname = find_device("/etc/mtab", st.st_dev);
+    if (subvol[0] && !devname) { /* we just find it is a btrfs subvol */
+	char parent[256];
+	char *tmp;
+
+	strcpy(parent, path);
+	tmp = strrchr(parent, '/');
+	if (tmp) {
+	    *tmp = '\0';
+	    fprintf(stderr, "%s is subvol, try its parent dir %s\n", path, parent);
+	    devname = get_devname(parent);
+	} else
+	    devname = NULL;
+    }
     if (!devname) {
-	/* Didn't find it in /proc/mounts, try /etc/mtab */
-	devname = find_device("/etc/mtab", st.st_dev);
+	/* Didn't find it in /etc/mtab, try /proc/mounts */
+	devname = find_device("/proc/mounts", st.st_dev);
     }
     if (!devname) {
 	fprintf(stderr, "%s: cannot find device for path %s\n", program, path);
diff --git a/libinstaller/syslxint.h b/libinstaller/syslxint.h
index ba3e501..7f8d304 100644
--- a/libinstaller/syslxint.h
+++ b/libinstaller/syslxint.h
@@ -89,6 +89,8 @@ struct patch_area {
     uint32_t checksum;
     uint16_t diroffset;
     uint16_t dirlen;
+    uint16_t subvoloffset;
+    uint16_t subvollen;
     uint16_t secptroffset;
     uint16_t secptrcnt;
 };



More information about the Syslinux-commits mailing list