summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlek Du <alek.du@intel.com>2010-01-13 15:06:15 +0800
committerH. Peter Anvin <hpa@zytor.com>2010-01-13 15:30:10 -0800
commit18f397e4f6d854798a967841c452adc1d8b3d1fc (patch)
tree1b8587c578731f5f86c924e819cf1c35173e49d2
parent44bad3a849a5f30186365eecf69722133775cee8 (diff)
downloadsyslinux-18f397e4f6d854798a967841c452adc1d8b3d1fc.tar.gz
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.
-rw-r--r--core/diskstart.inc21
-rw-r--r--core/fs/btrfs/btrfs.c100
-rw-r--r--core/fs/btrfs/btrfs.h7
-rw-r--r--core/include/core.h1
-rw-r--r--extlinux/main.c52
-rw-r--r--libinstaller/syslxint.h2
6 files changed, 156 insertions, 27 deletions
diff --git a/core/diskstart.inc b/core/diskstart.inc
index 3bffc896..ca627b1a 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 31e24f77..ee042601 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 8ec8afe3..eacdeb5f 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 1a9e1b96..f54fcf8f 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 05b390f3..c29e3b0b 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 ba3e501e..7f8d3041 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;
};