Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F3153510
main.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
20 KB
Referenced Files
None
Subscribers
None
main.c
View Options
/******************************************************************************
*******************************************************************************
**
** Copyright (C) Sistina Software, Inc. 1997-2003 All rights reserved.
** Copyright (C) 2004 Red Hat, Inc. All rights reserved.
**
** This copyrighted material is made available to anyone wishing to use,
** modify, copy, or redistribute it subject to the terms and conditions
** of the GNU General Public License v.2.
**
*******************************************************************************
******************************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <getopt.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <errno.h>
#include "global.h"
#include <linux/gfs_ondisk.h>
#include <linux/gfs_ioctl.h>
#include "osi_list.h"
#include "iddev.h"
#include "copyright.cf"
struct rglist_entry {
osi_list_t list;
struct gfs_rindex ri;
struct gfs_rgrp rg;
};
struct jilist_entry {
osi_list_t list;
struct gfs_jindex ji;
};
/*
* verbose: 0 = no messages, 1 = normal, 2 = everything
* test: 0 = normal, 1 = don't actually write data, but do everything else
* fspath: path to root of mounted GFS filesystem
* device: the device upon which the GFS filesystem is mounted
* fsoptions: the mount options used
* devsize: the size of the device (in filesystem blocks, rounded down)
* fssize: the size of the filesystem (in filesystem blocks, rounded down)
* override_device_size: if non-zero, this is used for the device size
*/
static int verbose = 1;
static int test = 0;
static char fspath[4096];
static char device[1024];
static char fsoptions[4096];
static uint64 devsize;
static uint64 fssize;
static uint64 override_device_size = 0;
/*
* fs_sb: the superblock read from the mounted filesystem
* rglist_current: list of resource groups currently making up the filesystem
* rglist_new: where we put the new resource groups to be written
* jilist_current: list of current journals in the filesystem
*/
static struct gfs_sb fs_sb;
static osi_list_decl(rglist_current);
static osi_list_decl(rglist_new);
static osi_list_decl(jilist_current);
/**
* device_geometry - Find out the size of a block device
* @device: The name of the device
*
* Returns: The size of the device in FS blocks
*/
static uint64 device_geometry(char *device)
{
int fd;
uint64 bytes;
int error;
if (override_device_size)
bytes = override_device_size;
else
{
fd = open(device, O_RDONLY);
if (fd < 0)
{
fprintf(stderr, "gfs_grow: can't open %s: %s\n",
device, strerror(errno));
exit(EXIT_FAILURE);
}
error = device_size(fd, &bytes);
if (error)
{
fprintf(stderr, "gfs_grow: can't determine size of %s: %s\n",
device, strerror(errno));
exit(EXIT_FAILURE);
}
close(fd);
}
if (bytes > (((uint64)2) << 40))
{
if (verbose)
printf("gfs_grow: warning: only using the first 2TB of this device\n");
bytes = (((uint64)2) << 40);
}
return bytes >> fs_sb.sb_bsize_shift;
}
/**
* jread - Read from journaled file using ioctl()
* @fd: The fd to read from
* @file: The file to read
* @buf: The buffer to fill
* @size: The amount of data to read
* @offset: The offset to read from
*
* Returns: Error code, or amount of data read
*/
int jread(int fd, unsigned int file, void *buf, uint64 size, uint64 *offset)
{
struct gfs_jio jt;
int ret;
jt.jio_file = file;
jt.jio_size = size;
jt.jio_offset = *offset;
jt.jio_data = buf;
ret = ioctl(fd, GFS_JREAD, &jt);
if (!ret)
{
ret = jt.jio_count;
(*offset) += ret;
}
return ret;
}
/**
* jwrite - Write to journaled file using ioctl()
* @fd: The fd to write to
* @file: The file to write
* @buf: The buffer to write
* @size: The amount of data to write
* @offset: The offset at which to write the data
*
* Returns: Error code, or the amount of data written
*/
int jwrite(int fd, unsigned int file, void *buf, uint64 size, uint64 *offset)
{
struct gfs_jio jt;
int ret;
jt.jio_file = file;
jt.jio_size = size;
jt.jio_offset = *offset;
jt.jio_data = buf;
ret = ioctl(fd, GFS_JWRITE, &jt);
if (!ret)
{
ret = jt.jio_count;
(*offset) += ret;
}
return ret;
}
/**
* filesystem_size - Calculate the size of the filesystem
*
* Reads the lists of journals and resource groups in order to
* work out where the last block of the filesystem is located.
*
* Returns: The calculated size
*/
static uint64 filesystem_size(void)
{
osi_list_t *tmp, *head;
struct rglist_entry *rgl;
struct jilist_entry *jil;
uint64 size = 0;
uint64 extent;
tmp = head = &rglist_current;
for(;;) {
tmp = tmp->next;
if (tmp == head)
break;
rgl = osi_list_entry(tmp, struct rglist_entry, list);
extent = rgl->ri.ri_addr + rgl->ri.ri_length + rgl->ri.ri_data;
if (extent > size)
size = extent;
}
tmp = head = &jilist_current;
for(;;) {
tmp = tmp->next;
if (tmp == head)
break;
jil = osi_list_entry(tmp, struct jilist_entry, list);
extent = jil->ji.ji_addr + jil->ji.ji_nsegment * fs_sb.sb_seg_size;
if (extent > size)
size = extent;
}
return size;
}
/**
* gfs_jientry - Get journal index entry
* @fd: The fd of the journal index
* @offset: The offset at which the journal entry appears
*
* Reads a single entry from the journal index file and
* adds it to the list of current journal entries.
*
* Returns: 1 on EOF, 0 otherwise
*/
static int get_jientry(int fd, uint64 *offset)
{
char buffer[sizeof(struct gfs_jindex)];
int len = jread(fd, GFS_HIDDEN_JINDEX, buffer, sizeof(struct gfs_jindex), offset);
struct jilist_entry *jil;
if (len != sizeof(struct gfs_jindex)) {
if (len == 0)
return 1;
fprintf(stderr, "Erk! Read odd size from jindex (%d)\n", len);
exit(EXIT_FAILURE);
}
if ((jil = malloc(sizeof(struct jilist_entry))) == NULL) {
perror("jilist_entry");
exit(EXIT_FAILURE);
}
memset(jil, 0, sizeof(struct jilist_entry));
gfs_jindex_in(&jil->ji, buffer);
osi_list_add(&jil->list, &jilist_current);
return 0;
}
/**
* read_journals - Read the whole journal index
* @fs_fd: An fd for some file or directory within the mounted GFS filesystem
*
*/
static void read_journals(int fs_fd)
{
uint64 offset = 0;
while(get_jientry(fs_fd, &offset) == 0) ;
}
/**
* get_rgrp - Read a single rindex entry
* @fd: The fd for the rindex file
* @offset: The offset at which to read the rindex entry
*
* Reads a single rindex entry and adds it to the list of current
* resource group entries.
*
* Returns: 1 on EOF, 0 otherwise
*/
static int get_rgrp(int fd, uint64 *offset)
{
char buffer[sizeof(struct gfs_rindex)];
int len = jread(fd, GFS_HIDDEN_RINDEX, buffer, sizeof(struct gfs_rindex), offset);
struct rglist_entry *rgl;
if (len != sizeof(struct gfs_rindex)) {
if (len == 0)
return 1;
fprintf(stderr, "Erk! Read odd size from rindex (%d)\n", len);
exit(EXIT_FAILURE);
}
if ((rgl = malloc(sizeof(struct rglist_entry))) == NULL) {
perror("rglist_entry");
exit(EXIT_FAILURE);
}
memset(rgl, 0, sizeof(struct rglist_entry));
gfs_rindex_in(&rgl->ri, buffer);
osi_list_add(&rgl->list, &rglist_current);
return 0;
}
/**
* read_rgrps - Reads the contents of the rindex file
* @fs_fd: An fd for any file or directory within the mounted GFS filesytem
*
*/
static void read_rgrps(int fs_fd)
{
uint64 offset = 0;
while(get_rgrp(fs_fd, &offset) == 0) ;
}
/**
* write_a_block - Write a block to the current device
* @where: The position to write the block (in filesystem blocks)
* @rg: The (optional) resource group to write
*
* Writes a single disk block to the device. It has a safety check which
* prevents it writing to the device at a position within the control of
* the active filesystem. If @rg is NULL, it writes a single block of
* zeros with a meta_header, otherwise the resource group is copied
* into the start of the block.
*/
static void write_a_block(uint64 where, struct gfs_rgrp *rg)
{
char buffer[4096];
uint64 fsoffset = where * (uint64)fs_sb.sb_bsize;
int fd = open(device, O_RDWR);
struct gfs_meta_header mh;
mh.mh_magic = GFS_MAGIC;
mh.mh_type = GFS_METATYPE_RB;
mh.mh_format = GFS_FORMAT_RB;
if (fd < 0) {
perror(device);
exit(EXIT_FAILURE);
}
if (where < fssize) {
fprintf(stderr, "Sanity check failed: Caught trying to write to live filesystem!\n");
exit(EXIT_FAILURE);
}
memset(buffer, 0, 4096);
if (rg)
gfs_rgrp_out(rg, buffer);
else
gfs_meta_header_out(&mh, buffer);
if (lseek(fd, fsoffset, SEEK_SET) != fsoffset) {
perror(device);
exit(EXIT_FAILURE);
}
if (write(fd, buffer, fs_sb.sb_bsize) != fs_sb.sb_bsize) {
perror("write_zero_block");
exit(EXIT_FAILURE);
}
close(fd);
}
/**
* write_whole_rgrp - Write a complete rgrp, including bitmaps
* @rgl: The information about the resource group
*
* Writes a complete rgrp, including any bitmap blocks required
* by calling write_a_block() a number of times. Calls sync() to
* ensure data really reached disk.
*/
static void write_whole_rgrp(struct rglist_entry *rgl)
{
uint32 l;
uint32 nzb = rgl->ri.ri_length;
uint64 addr = rgl->ri.ri_addr;
write_a_block(addr++, &rgl->rg);
for(l = 1; l < nzb; l++)
write_a_block(addr++, NULL);
sync();
}
/**
* get_length - Use stat() to get the length of a file
* @fd: The fd of the file whose length we wish to know
*
* Returns: The length
*/
static uint64 get_length(int fd, unsigned int file)
{
struct gfs_jio jt;
struct gfs_dinode di;
jt.jio_file = file;
jt.jio_offset = 0;
jt.jio_size = sizeof(struct gfs_dinode);
jt.jio_data = (char *)&di;
if (ioctl(fd, GFS_JSTAT, &jt) < 0) {
perror("stat");
fprintf(stderr, "Failed to get size of file. Aborting.\n");
exit(EXIT_FAILURE);
}
return di.di_size;
}
/**
* write_rindex - Writes new records to the end of the rindex file
* @fs_fd: A fd of any file or directory withint the GFS filesystem
*
* This is the critical function in expanding a filesystem. It does the
* actual write to the rindex which causes the GFS filesystem to see the
* new resource groups which were previously added.
*/
static void write_rindex(int fs_fd)
{
osi_list_t *tmp, *head;
struct rglist_entry *rgl;
char buffer[sizeof(struct gfs_rindex)];
uint64 orig_length;
uint64 offset;
offset = orig_length = get_length(fs_fd, GFS_HIDDEN_RINDEX);
/*
* This is the critical section.
* If things mess up here, it could be very difficult to put right
*/
tmp = head = &rglist_new;
for(;;) {
tmp = tmp->next;
if (tmp == head)
break;
rgl = osi_list_entry(tmp, struct rglist_entry, list);
gfs_rindex_out(&rgl->ri, buffer);
if (jwrite(fs_fd, GFS_HIDDEN_RINDEX, buffer, sizeof(struct gfs_rindex), &offset) != sizeof(struct gfs_rindex)) {
perror("write: rindex");
fprintf(stderr, "Aborting...\n");
exit(EXIT_FAILURE);
}
}
/*
* This is the end of the critical section
*/
return;
}
/**
* write_rgrps - Write the new resource groups to disk
* @fs_fd: An fd from any file or directory on the GFS mounted filesystem
*
* This first writes out the new resource group information to the
* area of the disk beyond the area the filesystem is currently
* using and then calls write_rindex() to make the filesystem see
* the newly written resource groups.
*/
static void write_rgrps(int fs_fd)
{
osi_list_t *tmp, *head;
struct rglist_entry *rgl;
tmp = head = &rglist_new;
for(;;) {
tmp = tmp->next;
if (tmp == head)
break;
rgl = osi_list_entry(tmp, struct rglist_entry, list);
write_whole_rgrp(rgl);
}
sync();
sync();
sync();
write_rindex(fs_fd);
sync();
sync();
sync();
}
/**
* gather_info - Gathers all the information about the existing filesystem
*
*/
static void gather_info(void)
{
int fd = open(fspath, O_RDONLY);
if (fd < 0) {
perror(fspath);
exit(EXIT_FAILURE);
}
if (ioctl(fd, GFS_GET_SUPER, &fs_sb) < 0) {
perror("ioctl: GFS_GET_SUPER");
exit(EXIT_FAILURE);
}
read_rgrps(fd);
read_journals(fd);
close(fd);
devsize = device_geometry(device);
fssize = filesystem_size();
}
/**
* print_rgrps - Print information about resource groups
* @lh: The list of resoruce groups to print
*
*/
static void print_rgrps(osi_list_t *lh)
{
osi_list_t *tmp, *head;
struct rglist_entry *rgl;
int n = 0;
tmp = head = lh;
for(;;) {
tmp = tmp->next;
if (tmp == head)
break;
rgl = osi_list_entry(tmp, struct rglist_entry, list);
n++;
printf("RI: Addr %"PRIu64", RgLen %u, "
"Start %"PRIu64", DataLen %u, BmapLen %u\n",
rgl->ri.ri_addr, rgl->ri.ri_length,
rgl->ri.ri_data1, rgl->ri.ri_data, rgl->ri.ri_bitbytes);
}
printf("RGRP: %d Resource groups in total\n", n);
}
/**
* print_journals - Print a list of journals
*
*/
static void print_journals(osi_list_t *lh)
{
osi_list_t *tmp, *head;
struct jilist_entry *jil;
int n = 0;
tmp = head = lh;
for(;;) {
tmp = tmp->next;
if (tmp == head)
break;
jil = osi_list_entry(tmp, struct jilist_entry, list);
n++;
printf("JI: Addr %"PRIu64" NumSeg %u SegSize %u\n", jil->ji.ji_addr,
jil->ji.ji_nsegment, fs_sb.sb_seg_size);
}
printf("JRNL: %d Journals in total\n", n);
}
/**
* print_info - Print out various bits of (interesting?) information
*
*/
static void print_info(void)
{
printf("FS: Mount Point: %s\n", fspath);
printf("FS: Device: %s\n", device);
printf("FS: Options: %s\n", fsoptions);
printf("FS: Size: %"PRIu64"\n", fssize);
if (verbose > 1) {
printf("RGRP: Current Resource Group List:\n");
print_rgrps(&rglist_current);
printf("JRNL: Current Journal List:\n");
print_journals(&jilist_current);
}
printf("DEV: Size: %"PRIu64"\n", devsize);
if (verbose > 1) {
printf("RGRP: New Resource Group List:\n");
print_rgrps(&rglist_new);
}
}
#define RGRP_STUFFED_BLKS(sb) (((sb)->sb_bsize - sizeof(struct gfs_rgrp)) * GFS_NBBY)
#define RGRP_BITMAP_BLKS(sb) (((sb)->sb_bsize - sizeof(struct gfs_meta_header)) * GFS_NBBY)
/**
* rgrp_length - Calculate the length of a resource group
* @size: The total size of the resource group
*
*/
uint64 rgrp_length(uint64 size)
{
uint64 bitbytes = RGRP_BITMAP_BLKS(&fs_sb) + 1;
uint64 stuff = RGRP_STUFFED_BLKS(&fs_sb) + 1;
uint64 blocks = 1;
if (size < stuff)
goto out;
size -= stuff;
while(size > bitbytes) {
blocks++;
size -= bitbytes;
}
if (size)
blocks++;
out:
return blocks;
}
/**
* make_rgrp - Make a new rglist_entry
* @offset: The offset at which the new rgrp will go
* @size: The size of the new rgrp
*
* Returns: The end of the new resource group
*/
uint64 make_rgrp(uint64 offset, uint64 size)
{
struct rglist_entry *rgl = malloc(sizeof(struct rglist_entry));
if (rgl == NULL)
exit(EXIT_FAILURE);
memset(rgl, 0, sizeof(struct rglist_entry));
rgl->ri.ri_addr = offset;
rgl->ri.ri_length = rgrp_length(size);
rgl->ri.ri_data1 = offset + rgl->ri.ri_length;
rgl->ri.ri_data = size - rgl->ri.ri_length;
/* Round down to nearest multiple of GFS_NBBY */
while(rgl->ri.ri_data & 0x03)
rgl->ri.ri_data--;
rgl->ri.ri_bitbytes = rgl->ri.ri_data / GFS_NBBY;
rgl->rg.rg_header.mh_magic = GFS_MAGIC;
rgl->rg.rg_header.mh_type = GFS_METATYPE_RG;
rgl->rg.rg_header.mh_format = GFS_FORMAT_RG;
rgl->rg.rg_free = rgl->ri.ri_data;
osi_list_add(&rgl->list, &rglist_new);
return offset + size;
}
/**
* create_rgrps - Create a list of the new rgrps
*
*/
static void create_rgrps(void)
{
uint64 space = devsize - fssize;
uint64 optimal_rgrp_size = RGRP_STUFFED_BLKS(&fs_sb) +
14 * RGRP_BITMAP_BLKS(&fs_sb) + 15;
uint64 rgrps = 1 + space / optimal_rgrp_size;
uint64 offset = fssize;
uint64 rgsize;
uint64 n;
rgsize = optimal_rgrp_size;
for(n = 0; n < rgrps; n++)
offset = make_rgrp(offset, (n != 0) ? rgsize :
(space - ((rgrps-1)*rgsize)));
if (offset > devsize) {
fprintf(stderr, "Calculation error: Out of bounds\n");
exit(EXIT_FAILURE);
}
}
/**
* update_fs - Actually perform the filesystem update
*
*/
static void update_fs(void)
{
int fd = open(fspath, O_RDONLY);
if (fd < 0) {
perror(fspath);
exit(EXIT_FAILURE);
}
if (verbose)
printf("Preparing to write new FS information...\n");
write_rgrps(fd);
if (verbose)
printf("Done.\n");
close(fd);
}
/**
* find_fs - Find the filesystem which the user specified
* @name: The name of a device or mount point
*
* Returns: 0 if the filesystem is located, 1 otherwise
*/
static int find_fs(char *name)
{
FILE *fp = fopen("/proc/mounts", "r");
char buffer[4096];
char fstype[80];
int fsdump, fspass;
if (fp == NULL) {
perror("open: /proc/mounts");
exit(EXIT_FAILURE);
}
while((fgets(buffer, 4095, fp)) != NULL) {
buffer[4095] = 0;
if (strstr(buffer, name) == 0)
continue;
if (sscanf(buffer, "%s %s %s %s %d %d", device, fspath, fstype,
fsoptions, &fsdump, &fspass) != 6)
continue;
if (strcmp(fstype, "gfs") != 0)
continue;
if ((strcmp(device, name) != 0) && (strcmp(fspath, name) != 0))
continue;
fclose(fp);
return 0;
}
fprintf(stderr, "GFS Filesystem %s not found\n", name);
fclose(fp);
return 1;
}
/**
* delete_rgrp_list - Delete a list of rgrps
* @list: The list to delete
*
*/
static void delete_rgrp_list(osi_list_t *list)
{
struct rglist_entry *rg;
while(!osi_list_empty(list)) {
rg = osi_list_entry(list->next, struct rglist_entry, list);
osi_list_del(&rg->list);
free(rg);
}
}
/**
* delete_jrnl_list - Delete a list of journals
* @list: the list to delete
*
*/
static void delete_jrnl_list(osi_list_t *list)
{
struct jilist_entry *ji;
while(!osi_list_empty(list)) {
ji = osi_list_entry(list->next, struct jilist_entry, list);
osi_list_del(&ji->list);
free(ji);
}
}
/**
* usage - Print out the usage message
*
* This function does not include documentation for the -D option
* since normal users have no use for it at all. The -D option is
* only for developers. It intended use is in combination with the
* -T flag to find out what the result would be of trying different
* device sizes without actually having to try them manually.
*/
static void usage(void)
{
fprintf(stdout,
"Usage:\n"
"\n"
"gfs_grow [options] /path/to/filesystem\n"
"\n"
"Options:\n"
" -h Usage information\n"
" -q Quiet, reduce verbosity\n"
" -T Test, do everything except update FS\n"
" -V Version information\n"
" -v Verbose, increase verbosity\n"
);
}
/**
* main - Tha main function
* @argc: The argument count
* @argv: The argument vector
*
* Runs through the filesystem expansion code for each of the specified
* filesystems. Each filesystem specified on the command line has the
* same options applied to it. You'll need to run the program multiple times
* if you want to use it on several different filesystems with different
* options for each. If you forget to specify a filesystem, then it is
* assumed that the program has run successfully, since its done everything
* asked of it, and it exits without printing a message.
*
* Returns: 0 on success, -1 otherwise
*/
int main(int argc, char *argv[])
{
int opt;
int error = 0;
while ((opt = getopt(argc, argv, "VD:hqTv?")) != EOF) {
switch(opt) {
case 'D': /* This option is for testing only */
override_device_size = atoi(optarg);
override_device_size <<= 20;
break;
case 'V':
printf("%s %s (built %s %s)\n", argv[0],
GFS_RELEASE_NAME, __DATE__, __TIME__);
printf("%s\n", REDHAT_COPYRIGHT);
exit(0);
case 'h':
usage();
exit(0);
case 'q':
if (verbose)
verbose--;
break;
case 'T':
test = 1;
break;
case 'v':
verbose++;
break;
case ':':
case '?':
/* Unknown flag */
fprintf(stderr, "Please use '-h' for usage.\n");
exit(EXIT_FAILURE);
default:
fprintf(stderr, "Bad programmer! You forgot"
" to catch the %c flag\n", opt);
exit(EXIT_FAILURE);
break;
}
}
if (optind == argc)
{
usage();
exit(EXIT_FAILURE);
}
while ((argc - optind) > 0) {
if (find_fs(argv[optind++])) {
error = 1;
continue;
}
gather_info();
if (fssize > devsize) {
error = 1;
fprintf(stderr, "Filesystem thinks device is bigger than it really is.... skipping\n");
continue;
}
if ((devsize - fssize) < 100) {
error = 1;
fprintf(stderr, "Device has grown by less than 100 blocks.... skipping\n");
continue;
}
create_rgrps();
if (verbose)
print_info();
if (!test)
update_fs();
delete_rgrp_list(&rglist_current);
delete_rgrp_list(&rglist_new);
delete_jrnl_list(&jilist_current);
}
return error;
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Tue, Feb 25, 8:57 PM (1 d, 5 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1464970
Default Alt Text
main.c (20 KB)
Attached To
Mode
rF Fence Agents
Attached
Detach File
Event Timeline
Log In to Comment