Compare commits
8 Commits
ec0d6231d8
...
dev
Author | SHA1 | Date | |
---|---|---|---|
0e60df0cfd | |||
062b2822dc | |||
8879fe1812 | |||
ec99a02d4f | |||
6aa4b2cf97 | |||
edbdab3a20 | |||
9b3fb864cd | |||
0cc121eb89 |
989
C/list.h
Normal file
989
C/list.h
Normal file
@@ -0,0 +1,989 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
|
||||||
|
/* adaptation of kernel's <linux/list.h>
|
||||||
|
* Main change is that I don't use READ_ONCE and WRITE_ONCE
|
||||||
|
* See https://www.kernel.org/doc/Documentation/memory-barriers.txt
|
||||||
|
*/
|
||||||
|
#ifndef __BR_LIST_H
|
||||||
|
#define __BR_LIST_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
/************ originally in <include/linux/types.h> */
|
||||||
|
struct list_head {
|
||||||
|
struct list_head *next, *prev;
|
||||||
|
};
|
||||||
|
struct hlist_head {
|
||||||
|
struct hlist_node *first;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hlist_node {
|
||||||
|
struct hlist_node *next, **pprev;
|
||||||
|
};
|
||||||
|
|
||||||
|
/************ originally in <include/linux/poison.h> */
|
||||||
|
#define LIST_POISON1 ((void *) 0x100)
|
||||||
|
#define LIST_POISON2 ((void *) 0x122)
|
||||||
|
|
||||||
|
/************ originally in <include/linux/kernel.h> */
|
||||||
|
#define container_of(ptr, type, member) \
|
||||||
|
((type *)((char *)(ptr) - offsetof(type, member)))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Circular doubly linked list implementation.
|
||||||
|
*
|
||||||
|
* Some of the internal functions ("__xxx") are useful when
|
||||||
|
* manipulating whole lists rather than single entries, as
|
||||||
|
* sometimes we already know the next/prev entries and we can
|
||||||
|
* generate better code by using them directly rather than
|
||||||
|
* using the generic single-entry routines.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||||
|
|
||||||
|
#define LIST_HEAD(name) \
|
||||||
|
struct list_head name = LIST_HEAD_INIT(name)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* INIT_LIST_HEAD - Initialize a list_head structure
|
||||||
|
* @list: list_head structure to be initialized.
|
||||||
|
*
|
||||||
|
* Initializes the list_head to point to itself. If it is a list header,
|
||||||
|
* the result is an empty list.
|
||||||
|
*/
|
||||||
|
static inline void INIT_LIST_HEAD(struct list_head *list)
|
||||||
|
{
|
||||||
|
list->next = list;
|
||||||
|
list->prev = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert a new entry between two known consecutive entries.
|
||||||
|
*
|
||||||
|
* This is only for internal list manipulation where we know
|
||||||
|
* the prev/next entries already!
|
||||||
|
*/
|
||||||
|
static inline void __list_add(struct list_head *new,
|
||||||
|
struct list_head *prev,
|
||||||
|
struct list_head *next)
|
||||||
|
{
|
||||||
|
next->prev = new;
|
||||||
|
new->next = next;
|
||||||
|
new->prev = prev;
|
||||||
|
prev->next = new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_add - add a new entry
|
||||||
|
* @new: new entry to be added
|
||||||
|
* @head: list head to add it after
|
||||||
|
*
|
||||||
|
* Insert a new entry after the specified head.
|
||||||
|
* This is good for implementing stacks.
|
||||||
|
*/
|
||||||
|
static inline void list_add(struct list_head *new, struct list_head *head)
|
||||||
|
{
|
||||||
|
__list_add(new, head, head->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_add_tail - add a new entry
|
||||||
|
* @new: new entry to be added
|
||||||
|
* @head: list head to add it before
|
||||||
|
*
|
||||||
|
* Insert a new entry before the specified head.
|
||||||
|
* This is useful for implementing queues.
|
||||||
|
*/
|
||||||
|
static inline void list_add_tail(struct list_head *new, struct list_head *head)
|
||||||
|
{
|
||||||
|
__list_add(new, head->prev, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Delete a list entry by making the prev/next entries
|
||||||
|
* point to each other.
|
||||||
|
*
|
||||||
|
* This is only for internal list manipulation where we know
|
||||||
|
* the prev/next entries already!
|
||||||
|
*/
|
||||||
|
static inline void __list_del(struct list_head * prev, struct list_head * next)
|
||||||
|
{
|
||||||
|
next->prev = prev;
|
||||||
|
prev->next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Delete a list entry and clear the 'prev' pointer.
|
||||||
|
*
|
||||||
|
* This is a special-purpose list clearing method used in the networking code
|
||||||
|
* for lists allocated as per-cpu, where we don't want to incur the extra
|
||||||
|
* WRITE_ONCE() overhead of a regular list_del_init(). The code that uses this
|
||||||
|
* needs to check the node 'prev' pointer instead of calling list_empty().
|
||||||
|
*/
|
||||||
|
static inline void __list_del_clearprev(struct list_head *entry)
|
||||||
|
{
|
||||||
|
__list_del(entry->prev, entry->next);
|
||||||
|
entry->prev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void __list_del_entry(struct list_head *entry)
|
||||||
|
{
|
||||||
|
__list_del(entry->prev, entry->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_del - deletes entry from list.
|
||||||
|
* @entry: the element to delete from the list.
|
||||||
|
* Note: list_empty() on entry does not return true after this, the entry is
|
||||||
|
* in an undefined state.
|
||||||
|
*/
|
||||||
|
static inline void list_del(struct list_head *entry)
|
||||||
|
{
|
||||||
|
__list_del_entry(entry);
|
||||||
|
entry->next = LIST_POISON1;
|
||||||
|
entry->prev = LIST_POISON2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_replace - replace old entry by new one
|
||||||
|
* @old : the element to be replaced
|
||||||
|
* @new : the new element to insert
|
||||||
|
*
|
||||||
|
* If @old was empty, it will be overwritten.
|
||||||
|
*/
|
||||||
|
static inline void list_replace(struct list_head *old,
|
||||||
|
struct list_head *new)
|
||||||
|
{
|
||||||
|
new->next = old->next;
|
||||||
|
new->next->prev = new;
|
||||||
|
new->prev = old->prev;
|
||||||
|
new->prev->next = new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_replace_init - replace old entry by new one and initialize the old one
|
||||||
|
* @old : the element to be replaced
|
||||||
|
* @new : the new element to insert
|
||||||
|
*
|
||||||
|
* If @old was empty, it will be overwritten.
|
||||||
|
*/
|
||||||
|
static inline void list_replace_init(struct list_head *old,
|
||||||
|
struct list_head *new)
|
||||||
|
{
|
||||||
|
list_replace(old, new);
|
||||||
|
INIT_LIST_HEAD(old);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_swap - replace entry1 with entry2 and re-add entry1 at entry2's position
|
||||||
|
* @entry1: the location to place entry2
|
||||||
|
* @entry2: the location to place entry1
|
||||||
|
*/
|
||||||
|
static inline void list_swap(struct list_head *entry1,
|
||||||
|
struct list_head *entry2)
|
||||||
|
{
|
||||||
|
struct list_head *pos = entry2->prev;
|
||||||
|
|
||||||
|
list_del(entry2);
|
||||||
|
list_replace(entry1, entry2);
|
||||||
|
if (pos == entry1)
|
||||||
|
pos = entry2;
|
||||||
|
list_add(entry1, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_del_init - deletes entry from list and reinitialize it.
|
||||||
|
* @entry: the element to delete from the list.
|
||||||
|
*/
|
||||||
|
static inline void list_del_init(struct list_head *entry)
|
||||||
|
{
|
||||||
|
__list_del_entry(entry);
|
||||||
|
INIT_LIST_HEAD(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_move - delete from one list and add as another's head
|
||||||
|
* @list: the entry to move
|
||||||
|
* @head: the head that will precede our entry
|
||||||
|
*/
|
||||||
|
static inline void list_move(struct list_head *list, struct list_head *head)
|
||||||
|
{
|
||||||
|
__list_del_entry(list);
|
||||||
|
list_add(list, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_move_tail - delete from one list and add as another's tail
|
||||||
|
* @list: the entry to move
|
||||||
|
* @head: the head that will follow our entry
|
||||||
|
*/
|
||||||
|
static inline void list_move_tail(struct list_head *list,
|
||||||
|
struct list_head *head)
|
||||||
|
{
|
||||||
|
__list_del_entry(list);
|
||||||
|
list_add_tail(list, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_bulk_move_tail - move a subsection of a list to its tail
|
||||||
|
* @head: the head that will follow our entry
|
||||||
|
* @first: first entry to move
|
||||||
|
* @last: last entry to move, can be the same as first
|
||||||
|
*
|
||||||
|
* Move all entries between @first and including @last before @head.
|
||||||
|
* All three entries must belong to the same linked list.
|
||||||
|
*/
|
||||||
|
static inline void list_bulk_move_tail(struct list_head *head,
|
||||||
|
struct list_head *first,
|
||||||
|
struct list_head *last)
|
||||||
|
{
|
||||||
|
first->prev->next = last->next;
|
||||||
|
last->next->prev = first->prev;
|
||||||
|
|
||||||
|
head->prev->next = first;
|
||||||
|
first->prev = head->prev;
|
||||||
|
|
||||||
|
last->next = head;
|
||||||
|
head->prev = last;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_is_first -- tests whether @list is the first entry in list @head
|
||||||
|
* @list: the entry to test
|
||||||
|
* @head: the head of the list
|
||||||
|
*/
|
||||||
|
static inline int list_is_first(const struct list_head *list,
|
||||||
|
const struct list_head *head)
|
||||||
|
{
|
||||||
|
return list->prev == head;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_is_last - tests whether @list is the last entry in list @head
|
||||||
|
* @list: the entry to test
|
||||||
|
* @head: the head of the list
|
||||||
|
*/
|
||||||
|
static inline int list_is_last(const struct list_head *list,
|
||||||
|
const struct list_head *head)
|
||||||
|
{
|
||||||
|
return list->next == head;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_empty - tests whether a list is empty
|
||||||
|
* @head: the list to test.
|
||||||
|
*/
|
||||||
|
static inline int list_empty(const struct list_head *head)
|
||||||
|
{
|
||||||
|
return head->next == head;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_rotate_left - rotate the list to the left
|
||||||
|
* @head: the head of the list
|
||||||
|
*/
|
||||||
|
static inline void list_rotate_left(struct list_head *head)
|
||||||
|
{
|
||||||
|
struct list_head *first;
|
||||||
|
|
||||||
|
if (!list_empty(head)) {
|
||||||
|
first = head->next;
|
||||||
|
list_move_tail(first, head);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_rotate_to_front() - Rotate list to specific item.
|
||||||
|
* @list: The desired new front of the list.
|
||||||
|
* @head: The head of the list.
|
||||||
|
*
|
||||||
|
* Rotates list so that @list becomes the new front of the list.
|
||||||
|
*/
|
||||||
|
static inline void list_rotate_to_front(struct list_head *list,
|
||||||
|
struct list_head *head)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Deletes the list head from the list denoted by @head and
|
||||||
|
* places it as the tail of @list, this effectively rotates the
|
||||||
|
* list so that @list is at the front.
|
||||||
|
*/
|
||||||
|
list_move_tail(head, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_is_singular - tests whether a list has just one entry.
|
||||||
|
* @head: the list to test.
|
||||||
|
*/
|
||||||
|
static inline int list_is_singular(const struct list_head *head)
|
||||||
|
{
|
||||||
|
return !list_empty(head) && (head->next == head->prev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void __list_cut_position(struct list_head *list,
|
||||||
|
struct list_head *head, struct list_head *entry)
|
||||||
|
{
|
||||||
|
struct list_head *new_first = entry->next;
|
||||||
|
list->next = head->next;
|
||||||
|
list->next->prev = list;
|
||||||
|
list->prev = entry;
|
||||||
|
entry->next = list;
|
||||||
|
head->next = new_first;
|
||||||
|
new_first->prev = head;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_cut_position - cut a list into two
|
||||||
|
* @list: a new list to add all removed entries
|
||||||
|
* @head: a list with entries
|
||||||
|
* @entry: an entry within head, could be the head itself
|
||||||
|
* and if so we won't cut the list
|
||||||
|
*
|
||||||
|
* This helper moves the initial part of @head, up to and
|
||||||
|
* including @entry, from @head to @list. You should
|
||||||
|
* pass on @entry an element you know is on @head. @list
|
||||||
|
* should be an empty list or a list you do not care about
|
||||||
|
* losing its data.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
static inline void list_cut_position(struct list_head *list,
|
||||||
|
struct list_head *head, struct list_head *entry)
|
||||||
|
{
|
||||||
|
if (list_empty(head))
|
||||||
|
return;
|
||||||
|
if (list_is_singular(head) &&
|
||||||
|
(head->next != entry && head != entry))
|
||||||
|
return;
|
||||||
|
if (entry == head)
|
||||||
|
INIT_LIST_HEAD(list);
|
||||||
|
else
|
||||||
|
__list_cut_position(list, head, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_cut_before - cut a list into two, before given entry
|
||||||
|
* @list: a new list to add all removed entries
|
||||||
|
* @head: a list with entries
|
||||||
|
* @entry: an entry within head, could be the head itself
|
||||||
|
*
|
||||||
|
* This helper moves the initial part of @head, up to but
|
||||||
|
* excluding @entry, from @head to @list. You should pass
|
||||||
|
* in @entry an element you know is on @head. @list should
|
||||||
|
* be an empty list or a list you do not care about losing
|
||||||
|
* its data.
|
||||||
|
* If @entry == @head, all entries on @head are moved to
|
||||||
|
* @list.
|
||||||
|
*/
|
||||||
|
static inline void list_cut_before(struct list_head *list,
|
||||||
|
struct list_head *head,
|
||||||
|
struct list_head *entry)
|
||||||
|
{
|
||||||
|
if (head->next == entry) {
|
||||||
|
INIT_LIST_HEAD(list);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
list->next = head->next;
|
||||||
|
list->next->prev = list;
|
||||||
|
list->prev = entry->prev;
|
||||||
|
list->prev->next = list;
|
||||||
|
head->next = entry;
|
||||||
|
entry->prev = head;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void __list_splice(const struct list_head *list,
|
||||||
|
struct list_head *prev,
|
||||||
|
struct list_head *next)
|
||||||
|
{
|
||||||
|
struct list_head *first = list->next;
|
||||||
|
struct list_head *last = list->prev;
|
||||||
|
|
||||||
|
first->prev = prev;
|
||||||
|
prev->next = first;
|
||||||
|
|
||||||
|
last->next = next;
|
||||||
|
next->prev = last;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_splice - join two lists, this is designed for stacks
|
||||||
|
* @list: the new list to add.
|
||||||
|
* @head: the place to add it in the first list.
|
||||||
|
*/
|
||||||
|
static inline void list_splice(const struct list_head *list,
|
||||||
|
struct list_head *head)
|
||||||
|
{
|
||||||
|
if (!list_empty(list))
|
||||||
|
__list_splice(list, head, head->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_splice_tail - join two lists, each list being a queue
|
||||||
|
* @list: the new list to add.
|
||||||
|
* @head: the place to add it in the first list.
|
||||||
|
*/
|
||||||
|
static inline void list_splice_tail(struct list_head *list,
|
||||||
|
struct list_head *head)
|
||||||
|
{
|
||||||
|
if (!list_empty(list))
|
||||||
|
__list_splice(list, head->prev, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_splice_init - join two lists and reinitialise the emptied list.
|
||||||
|
* @list: the new list to add.
|
||||||
|
* @head: the place to add it in the first list.
|
||||||
|
*
|
||||||
|
* The list at @list is reinitialised
|
||||||
|
*/
|
||||||
|
static inline void list_splice_init(struct list_head *list,
|
||||||
|
struct list_head *head)
|
||||||
|
{
|
||||||
|
if (!list_empty(list)) {
|
||||||
|
__list_splice(list, head, head->next);
|
||||||
|
INIT_LIST_HEAD(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_splice_tail_init - join two lists and reinitialise the emptied list
|
||||||
|
* @list: the new list to add.
|
||||||
|
* @head: the place to add it in the first list.
|
||||||
|
*
|
||||||
|
* Each of the lists is a queue.
|
||||||
|
* The list at @list is reinitialised
|
||||||
|
*/
|
||||||
|
static inline void list_splice_tail_init(struct list_head *list,
|
||||||
|
struct list_head *head)
|
||||||
|
{
|
||||||
|
if (!list_empty(list)) {
|
||||||
|
__list_splice(list, head->prev, head);
|
||||||
|
INIT_LIST_HEAD(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_entry - get the struct for this entry
|
||||||
|
* @ptr: the &struct list_head pointer.
|
||||||
|
* @type: the type of the struct this is embedded in.
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*/
|
||||||
|
#define list_entry(ptr, type, member) \
|
||||||
|
container_of(ptr, type, member)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_first_entry - get the first element from a list
|
||||||
|
* @ptr: the list head to take the element from.
|
||||||
|
* @type: the type of the struct this is embedded in.
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*
|
||||||
|
* Note, that list is expected to be not empty.
|
||||||
|
*/
|
||||||
|
#define list_first_entry(ptr, type, member) \
|
||||||
|
list_entry((ptr)->next, type, member)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_last_entry - get the last element from a list
|
||||||
|
* @ptr: the list head to take the element from.
|
||||||
|
* @type: the type of the struct this is embedded in.
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*
|
||||||
|
* Note, that list is expected to be not empty.
|
||||||
|
*/
|
||||||
|
#define list_last_entry(ptr, type, member) \
|
||||||
|
list_entry((ptr)->prev, type, member)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_first_entry_or_null - get the first element from a list
|
||||||
|
* @ptr: the list head to take the element from.
|
||||||
|
* @type: the type of the struct this is embedded in.
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*
|
||||||
|
* Note that if the list is empty, it returns NULL.
|
||||||
|
*/
|
||||||
|
#define list_first_entry_or_null(ptr, type, member) ({ \
|
||||||
|
struct list_head *head__ = (ptr); \
|
||||||
|
struct list_head *pos__ = head__->next; \
|
||||||
|
pos__ != head__ ? list_entry(pos__, type, member) : NULL; \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_next_entry - get the next element in list
|
||||||
|
* @pos: the type * to cursor
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*/
|
||||||
|
#define list_next_entry(pos, member) \
|
||||||
|
list_entry((pos)->member.next, __typeof__(*(pos)), member)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_prev_entry - get the prev element in list
|
||||||
|
* @pos: the type * to cursor
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*/
|
||||||
|
#define list_prev_entry(pos, member) \
|
||||||
|
list_entry((pos)->member.prev, __typeof__(*(pos)), member)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each - iterate over a list
|
||||||
|
* @pos: the &struct list_head to use as a loop cursor.
|
||||||
|
* @head: the head for your list.
|
||||||
|
*/
|
||||||
|
#define list_for_each(pos, head) \
|
||||||
|
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each_continue - continue iteration over a list
|
||||||
|
* @pos: the &struct list_head to use as a loop cursor.
|
||||||
|
* @head: the head for your list.
|
||||||
|
*
|
||||||
|
* Continue to iterate over a list, continuing after the current position.
|
||||||
|
*/
|
||||||
|
#define list_for_each_continue(pos, head) \
|
||||||
|
for (pos = pos->next; pos != (head); pos = pos->next)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each_prev - iterate over a list backwards
|
||||||
|
* @pos: the &struct list_head to use as a loop cursor.
|
||||||
|
* @head: the head for your list.
|
||||||
|
*/
|
||||||
|
#define list_for_each_prev(pos, head) \
|
||||||
|
for (pos = (head)->prev; pos != (head); pos = pos->prev)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each_safe - iterate over a list safe against removal of list entry
|
||||||
|
* @pos: the &struct list_head to use as a loop cursor.
|
||||||
|
* @n: another &struct list_head to use as temporary storage
|
||||||
|
* @head: the head for your list.
|
||||||
|
*/
|
||||||
|
#define list_for_each_safe(pos, n, head) \
|
||||||
|
for (pos = (head)->next, n = pos->next; pos != (head); \
|
||||||
|
pos = n, n = pos->next)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
|
||||||
|
* @pos: the &struct list_head to use as a loop cursor.
|
||||||
|
* @n: another &struct list_head to use as temporary storage
|
||||||
|
* @head: the head for your list.
|
||||||
|
*/
|
||||||
|
#define list_for_each_prev_safe(pos, n, head) \
|
||||||
|
for (pos = (head)->prev, n = pos->prev; \
|
||||||
|
pos != (head); \
|
||||||
|
pos = n, n = pos->prev)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_entry_is_head - test if the entry points to the head of the list
|
||||||
|
* @pos: the type * to cursor
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*/
|
||||||
|
#define list_entry_is_head(pos, head, member) \
|
||||||
|
(&pos->member == (head))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each_entry - iterate over list of given type
|
||||||
|
* @pos: the type * to use as a loop cursor.
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*/
|
||||||
|
#define list_for_each_entry(pos, head, member) \
|
||||||
|
for (pos = list_first_entry(head, __typeof__(*pos), member); \
|
||||||
|
!list_entry_is_head(pos, head, member); \
|
||||||
|
pos = list_next_entry(pos, member))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each_entry_reverse - iterate backwards over list of given type.
|
||||||
|
* @pos: the type * to use as a loop cursor.
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*/
|
||||||
|
#define list_for_each_entry_reverse(pos, head, member) \
|
||||||
|
for (pos = list_last_entry(head, __typeof__(*pos), member); \
|
||||||
|
!list_entry_is_head(pos, head, member); \
|
||||||
|
pos = list_prev_entry(pos, member))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
|
||||||
|
* @pos: the type * to use as a start point
|
||||||
|
* @head: the head of the list
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*
|
||||||
|
* Prepares a pos entry for use as a start point in list_for_each_entry_continue().
|
||||||
|
*/
|
||||||
|
#define list_prepare_entry(pos, head, member) \
|
||||||
|
((pos) ? : list_entry(head, __typeof__(*pos), member))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each_entry_continue - continue iteration over list of given type
|
||||||
|
* @pos: the type * to use as a loop cursor.
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*
|
||||||
|
* Continue to iterate over list of given type, continuing after
|
||||||
|
* the current position.
|
||||||
|
*/
|
||||||
|
#define list_for_each_entry_continue(pos, head, member) \
|
||||||
|
for (pos = list_next_entry(pos, member); \
|
||||||
|
!list_entry_is_head(pos, head, member); \
|
||||||
|
pos = list_next_entry(pos, member))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each_entry_continue_reverse - iterate backwards from the given point
|
||||||
|
* @pos: the type * to use as a loop cursor.
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*
|
||||||
|
* Start to iterate over list of given type backwards, continuing after
|
||||||
|
* the current position.
|
||||||
|
*/
|
||||||
|
#define list_for_each_entry_continue_reverse(pos, head, member) \
|
||||||
|
for (pos = list_prev_entry(pos, member); \
|
||||||
|
!list_entry_is_head(pos, head, member); \
|
||||||
|
pos = list_prev_entry(pos, member))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each_entry_from - iterate over list of given type from the current point
|
||||||
|
* @pos: the type * to use as a loop cursor.
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*
|
||||||
|
* Iterate over list of given type, continuing from current position.
|
||||||
|
*/
|
||||||
|
#define list_for_each_entry_from(pos, head, member) \
|
||||||
|
for (; !list_entry_is_head(pos, head, member); \
|
||||||
|
pos = list_next_entry(pos, member))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each_entry_from_reverse - iterate backwards over list of given type
|
||||||
|
* from the current point
|
||||||
|
* @pos: the type * to use as a loop cursor.
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*
|
||||||
|
* Iterate backwards over list of given type, continuing from current position.
|
||||||
|
*/
|
||||||
|
#define list_for_each_entry_from_reverse(pos, head, member) \
|
||||||
|
for (; !list_entry_is_head(pos, head, member); \
|
||||||
|
pos = list_prev_entry(pos, member))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
||||||
|
* @pos: the type * to use as a loop cursor.
|
||||||
|
* @n: another type * to use as temporary storage
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*/
|
||||||
|
#define list_for_each_entry_safe(pos, n, head, member) \
|
||||||
|
for (pos = list_first_entry(head, __typeof__(*pos), member), \
|
||||||
|
n = list_next_entry(pos, member); \
|
||||||
|
!list_entry_is_head(pos, head, member); \
|
||||||
|
pos = n, n = list_next_entry(n, member))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each_entry_safe_continue - continue list iteration safe against removal
|
||||||
|
* @pos: the type * to use as a loop cursor.
|
||||||
|
* @n: another type * to use as temporary storage
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*
|
||||||
|
* Iterate over list of given type, continuing after current point,
|
||||||
|
* safe against removal of list entry.
|
||||||
|
*/
|
||||||
|
#define list_for_each_entry_safe_continue(pos, n, head, member) \
|
||||||
|
for (pos = list_next_entry(pos, member), \
|
||||||
|
n = list_next_entry(pos, member); \
|
||||||
|
!list_entry_is_head(pos, head, member); \
|
||||||
|
pos = n, n = list_next_entry(n, member))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each_entry_safe_from - iterate over list from current point safe against removal
|
||||||
|
* @pos: the type * to use as a loop cursor.
|
||||||
|
* @n: another type * to use as temporary storage
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*
|
||||||
|
* Iterate over list of given type from current point, safe against
|
||||||
|
* removal of list entry.
|
||||||
|
*/
|
||||||
|
#define list_for_each_entry_safe_from(pos, n, head, member) \
|
||||||
|
for (n = list_next_entry(pos, member); \
|
||||||
|
!list_entry_is_head(pos, head, member); \
|
||||||
|
pos = n, n = list_next_entry(n, member))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
|
||||||
|
* @pos: the type * to use as a loop cursor.
|
||||||
|
* @n: another type * to use as temporary storage
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*
|
||||||
|
* Iterate backwards over list of given type, safe against removal
|
||||||
|
* of list entry.
|
||||||
|
*/
|
||||||
|
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
|
||||||
|
for (pos = list_last_entry(head, __typeof__(*pos), member), \
|
||||||
|
n = list_prev_entry(pos, member); \
|
||||||
|
!list_entry_is_head(pos, head, member); \
|
||||||
|
pos = n, n = list_prev_entry(n, member))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* list_safe_reset_next - reset a stale list_for_each_entry_safe loop
|
||||||
|
* @pos: the loop cursor used in the list_for_each_entry_safe loop
|
||||||
|
* @n: temporary storage used in list_for_each_entry_safe
|
||||||
|
* @member: the name of the list_head within the struct.
|
||||||
|
*
|
||||||
|
* list_safe_reset_next is not safe to use in general if the list may be
|
||||||
|
* modified concurrently (eg. the lock is dropped in the loop body). An
|
||||||
|
* exception to this is if the cursor element (pos) is pinned in the list,
|
||||||
|
* and list_safe_reset_next is called after re-taking the lock and before
|
||||||
|
* completing the current iteration of the loop body.
|
||||||
|
*/
|
||||||
|
#define list_safe_reset_next(pos, n, member) \
|
||||||
|
n = list_next_entry(pos, member)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Double linked lists with a single pointer list head.
|
||||||
|
* Mostly useful for hash tables where the two pointer list head is
|
||||||
|
* too wasteful.
|
||||||
|
* You lose the ability to access the tail in O(1).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define HLIST_HEAD_INIT { .first = NULL }
|
||||||
|
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
|
||||||
|
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
|
||||||
|
static inline void INIT_HLIST_NODE(struct hlist_node *h)
|
||||||
|
{
|
||||||
|
h->next = NULL;
|
||||||
|
h->pprev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_unhashed - Has node been removed from list and reinitialized?
|
||||||
|
* @h: Node to be checked
|
||||||
|
*
|
||||||
|
* Not that not all removal functions will leave a node in unhashed
|
||||||
|
* state. For example, hlist_nulls_del_init_rcu() does leave the
|
||||||
|
* node in unhashed state, but hlist_nulls_del() does not.
|
||||||
|
*/
|
||||||
|
static inline int hlist_unhashed(const struct hlist_node *h)
|
||||||
|
{
|
||||||
|
return !h->pprev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_unhashed_lockless - Version of hlist_unhashed for lockless use
|
||||||
|
* @h: Node to be checked
|
||||||
|
*
|
||||||
|
* This variant of hlist_unhashed() must be used in lockless contexts
|
||||||
|
* to avoid potential load-tearing. The READ_ONCE() is paired with the
|
||||||
|
* various WRITE_ONCE() in hlist helpers that are defined below.
|
||||||
|
*/
|
||||||
|
static inline int hlist_unhashed_lockless(const struct hlist_node *h)
|
||||||
|
{
|
||||||
|
return !h->pprev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_empty - Is the specified hlist_head structure an empty hlist?
|
||||||
|
* @h: Structure to check.
|
||||||
|
*/
|
||||||
|
static inline int hlist_empty(const struct hlist_head *h)
|
||||||
|
{
|
||||||
|
return !h->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void __hlist_del(struct hlist_node *n)
|
||||||
|
{
|
||||||
|
struct hlist_node *next = n->next;
|
||||||
|
struct hlist_node **pprev = n->pprev;
|
||||||
|
|
||||||
|
*pprev = next;
|
||||||
|
if (next)
|
||||||
|
next->pprev = pprev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_del - Delete the specified hlist_node from its list
|
||||||
|
* @n: Node to delete.
|
||||||
|
*
|
||||||
|
* Note that this function leaves the node in hashed state. Use
|
||||||
|
* hlist_del_init() or similar instead to unhash @n.
|
||||||
|
*/
|
||||||
|
static inline void hlist_del(struct hlist_node *n)
|
||||||
|
{
|
||||||
|
__hlist_del(n);
|
||||||
|
n->next = LIST_POISON1;
|
||||||
|
n->pprev = LIST_POISON2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_del_init - Delete the specified hlist_node from its list and initialize
|
||||||
|
* @n: Node to delete.
|
||||||
|
*
|
||||||
|
* Note that this function leaves the node in unhashed state.
|
||||||
|
*/
|
||||||
|
static inline void hlist_del_init(struct hlist_node *n)
|
||||||
|
{
|
||||||
|
if (!hlist_unhashed(n)) {
|
||||||
|
__hlist_del(n);
|
||||||
|
INIT_HLIST_NODE(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_add_head - add a new entry at the beginning of the hlist
|
||||||
|
* @n: new entry to be added
|
||||||
|
* @h: hlist head to add it after
|
||||||
|
*
|
||||||
|
* Insert a new entry after the specified head.
|
||||||
|
* This is good for implementing stacks.
|
||||||
|
*/
|
||||||
|
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
|
||||||
|
{
|
||||||
|
struct hlist_node *first = h->first;
|
||||||
|
n->next = first;
|
||||||
|
if (first)
|
||||||
|
first->pprev = &n->next;
|
||||||
|
h->first = n;
|
||||||
|
n->pprev = &h->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_add_before - add a new entry before the one specified
|
||||||
|
* @n: new entry to be added
|
||||||
|
* @next: hlist node to add it before, which must be non-NULL
|
||||||
|
*/
|
||||||
|
static inline void hlist_add_before(struct hlist_node *n,
|
||||||
|
struct hlist_node *next)
|
||||||
|
{
|
||||||
|
n->pprev = next->pprev;
|
||||||
|
n->next = next;
|
||||||
|
next->pprev = &n->next;
|
||||||
|
*(n->pprev) = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_add_behind - add a new entry after the one specified
|
||||||
|
* @n: new entry to be added
|
||||||
|
* @prev: hlist node to add it after, which must be non-NULL
|
||||||
|
*/
|
||||||
|
static inline void hlist_add_behind(struct hlist_node *n,
|
||||||
|
struct hlist_node *prev)
|
||||||
|
{
|
||||||
|
n->next = prev->next;
|
||||||
|
prev->next = n;
|
||||||
|
n->pprev = &prev->next;
|
||||||
|
|
||||||
|
if (n->next)
|
||||||
|
n->next->pprev = &n->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_add_fake - create a fake hlist consisting of a single headless node
|
||||||
|
* @n: Node to make a fake list out of
|
||||||
|
*
|
||||||
|
* This makes @n appear to be its own predecessor on a headless hlist.
|
||||||
|
* The point of this is to allow things like hlist_del() to work correctly
|
||||||
|
* in cases where there is no list.
|
||||||
|
*/
|
||||||
|
static inline void hlist_add_fake(struct hlist_node *n)
|
||||||
|
{
|
||||||
|
n->pprev = &n->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_fake: Is this node a fake hlist?
|
||||||
|
* @h: Node to check for being a self-referential fake hlist.
|
||||||
|
*/
|
||||||
|
static inline bool hlist_fake(struct hlist_node *h)
|
||||||
|
{
|
||||||
|
return h->pprev == &h->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_is_singular_node - is node the only element of the specified hlist?
|
||||||
|
* @n: Node to check for singularity.
|
||||||
|
* @h: Header for potentially singular list.
|
||||||
|
*
|
||||||
|
* Check whether the node is the only node of the head without
|
||||||
|
* accessing head, thus avoiding unnecessary cache misses.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
hlist_is_singular_node(struct hlist_node *n, struct hlist_head *h)
|
||||||
|
{
|
||||||
|
return !n->next && n->pprev == &h->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_move_list - Move an hlist
|
||||||
|
* @old: hlist_head for old list.
|
||||||
|
* @new: hlist_head for new list.
|
||||||
|
*
|
||||||
|
* Move a list from one list head to another. Fixup the pprev
|
||||||
|
* reference of the first entry if it exists.
|
||||||
|
*/
|
||||||
|
static inline void hlist_move_list(struct hlist_head *old,
|
||||||
|
struct hlist_head *new)
|
||||||
|
{
|
||||||
|
new->first = old->first;
|
||||||
|
if (new->first)
|
||||||
|
new->first->pprev = &new->first;
|
||||||
|
old->first = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
|
||||||
|
|
||||||
|
#define hlist_for_each(pos, head) \
|
||||||
|
for (pos = (head)->first; pos ; pos = pos->next)
|
||||||
|
|
||||||
|
#define hlist_for_each_safe(pos, n, head) \
|
||||||
|
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
|
||||||
|
pos = n)
|
||||||
|
|
||||||
|
#define hlist_entry_safe(ptr, type, member) \
|
||||||
|
({ __typeof__(ptr) ____ptr = (ptr); \
|
||||||
|
____ptr ? hlist_entry(____ptr, type, member) : NULL; \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_for_each_entry - iterate over list of given type
|
||||||
|
* @pos: the type * to use as a loop cursor.
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the hlist_node within the struct.
|
||||||
|
*/
|
||||||
|
#define hlist_for_each_entry(pos, head, member) \
|
||||||
|
for (pos = hlist_entry_safe((head)->first, __typeof__(*(pos)), member); \
|
||||||
|
pos; \
|
||||||
|
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_for_each_entry_continue - iterate over a hlist continuing after current point
|
||||||
|
* @pos: the type * to use as a loop cursor.
|
||||||
|
* @member: the name of the hlist_node within the struct.
|
||||||
|
*/
|
||||||
|
#define hlist_for_each_entry_continue(pos, member) \
|
||||||
|
for (pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member); \
|
||||||
|
pos; \
|
||||||
|
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_for_each_entry_from - iterate over a hlist continuing from current point
|
||||||
|
* @pos: the type * to use as a loop cursor.
|
||||||
|
* @member: the name of the hlist_node within the struct.
|
||||||
|
*/
|
||||||
|
#define hlist_for_each_entry_from(pos, member) \
|
||||||
|
for (; pos; \
|
||||||
|
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
||||||
|
* @pos: the type * to use as a loop cursor.
|
||||||
|
* @n: a &struct hlist_node to use as temporary storage
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the hlist_node within the struct.
|
||||||
|
*/
|
||||||
|
#define hlist_for_each_entry_safe(pos, n, head, member) \
|
||||||
|
for (pos = hlist_entry_safe((head)->first, typeof(*pos), member); \
|
||||||
|
pos && ({ n = pos->member.next; 1; }); \
|
||||||
|
pos = hlist_entry_safe(n, typeof(*pos), member))
|
||||||
|
|
||||||
|
#endif
|
@@ -3,8 +3,6 @@
|
|||||||
#### bash
|
#### bash
|
||||||
|
|
||||||
- **trans.sh**: a [linguee.com](https://linguee.com) based command-line translator.
|
- **trans.sh**: a [linguee.com](https://linguee.com) based command-line translator.
|
||||||
|
|
||||||
- **sync.sh**: a rsync/ssh backup tool.
|
- **sync.sh**: a rsync/ssh backup tool.
|
||||||
- **sync-conf-example.sh**: configuration example.
|
- **sync-conf-example.sh**: configuration example.
|
||||||
|
- **dup-live-disk.sh**: duplicate (possibly live) disk partitions.
|
||||||
- **dup-live-disk.sh**: copy live partitions to backup disk.
|
|
||||||
|
515
bash/dup-live-disk.sh
Normal file → Executable file
515
bash/dup-live-disk.sh
Normal file → Executable file
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# dup-live-disk.sh - duplicate live system partitions on new disk.
|
# dup-live-disk.sh - duplicate (possibly live) system partitions
|
||||||
#
|
#
|
||||||
# (C) Bruno Raoult ("br"), 2007-2021
|
# (C) Bruno Raoult ("br"), 2007-2021
|
||||||
# Licensed under the Mozilla Public License (MPL) version 2.0.
|
# Licensed under the Mozilla Public License (MPL) version 2.0.
|
||||||
@@ -11,63 +11,488 @@
|
|||||||
#
|
#
|
||||||
# SPDX-License-Identifier: MPL-2.0 <https://spdx.org/licenses/MPL-2.0.html>
|
# SPDX-License-Identifier: MPL-2.0 <https://spdx.org/licenses/MPL-2.0.html>
|
||||||
#
|
#
|
||||||
# This script will not work for all situations, I strongly suggest you
|
#%MAN_BEGIN%
|
||||||
|
# NAME
|
||||||
|
# dup-live-disk.sh - duplicate (possibly live) system partitions
|
||||||
|
#
|
||||||
|
# SYNOPSIS
|
||||||
|
# dup-live-disk.sh [OPTIONS] [SRC] DST
|
||||||
|
#
|
||||||
|
# DESCRIPTION
|
||||||
|
# Duplicate SRC disk partitions to same structured DST disk ones.
|
||||||
|
# if SRC is omitted, the running system disk (where root partition
|
||||||
|
# resides) will be used.
|
||||||
|
# Both SRC and DST *must* have same partition base LABELs - as 'LABEL'
|
||||||
|
# field for lsblk(1) and blkid(1), with an ending character (unique per
|
||||||
|
# disk) to differentiate them.
|
||||||
|
# For example, if partitions base labels are 'root', 'export', and 'swap',
|
||||||
|
# SRC disk the ending character '1' and DST disk the character '2', SRC
|
||||||
|
# partitions must be 'root1', 'export1, and 'swap1', and DST partitions
|
||||||
|
# must be 'root2', 'export2, and 'swap2'.
|
||||||
|
#
|
||||||
|
# OPTIONS
|
||||||
|
# -a, --autofs=DIR
|
||||||
|
# Use DIR as autofs "LABEL-based" automount. See AUTOFS below. Default
|
||||||
|
# is /mnt.
|
||||||
|
#
|
||||||
|
# -c, --copy=ACTION
|
||||||
|
# ACTION can be 'yes' (all eligible partitions will be copied), 'no'
|
||||||
|
# (no partition will be copied), or 'ask' (will ask for all eligible
|
||||||
|
# partitions). Default is 'no'.
|
||||||
|
#
|
||||||
|
# -d, --dry-run
|
||||||
|
# Dry-run: nothing will be really be done.
|
||||||
|
#
|
||||||
|
# -g, --grub
|
||||||
|
# Install grub on destination disk.
|
||||||
|
# Warning: Only works if root partition contains all necessary for
|
||||||
|
# grub: /boot, /usr, etc...
|
||||||
|
#
|
||||||
|
# -h, --help
|
||||||
|
# Display short help and exit.
|
||||||
|
#
|
||||||
|
# -m, --man
|
||||||
|
# Display a "man-like" description and exit.
|
||||||
|
#
|
||||||
|
# --mariadb
|
||||||
|
# Stop mysql/mariadb before effective copies, restart after.
|
||||||
|
#
|
||||||
|
# -r, --root=PARTNUM
|
||||||
|
# Mandatory if SRC is provided, forbidden otherwise.
|
||||||
|
# PARTNUM is root partition number on SRC disk.
|
||||||
|
#
|
||||||
|
# EXAMPLES
|
||||||
|
# Copy sda to sdb, root partition is partition (sda1/sdb1)
|
||||||
|
# $ sudo dup-live-disk.sh --root 1 sda sdb
|
||||||
|
#
|
||||||
|
# Copy live system (where / is mounted) to sdb
|
||||||
|
# $ sudo dup-live-disk.sh sdb
|
||||||
|
#
|
||||||
|
# BUGS
|
||||||
|
# * Cannot generate grub with a separate /boot partition.
|
||||||
|
# * This script will not work for all situations, I strongly suggest you
|
||||||
# don't use it if you don't *fully* understand it.
|
# don't use it if you don't *fully* understand it.
|
||||||
|
# * Extended attributes are not preserved (easy fix, but I cannot test)
|
||||||
|
#
|
||||||
|
# TODO
|
||||||
|
# * Write about autofs configuration.
|
||||||
|
# * Log levels
|
||||||
|
#%MAN_END%
|
||||||
|
|
||||||
# dest device (used for grub)
|
# command line
|
||||||
DSTDISK=/dev/sdb
|
SCRIPT="$0"
|
||||||
|
CMD="${0##*/}"
|
||||||
|
|
||||||
# partitions suffixes, for source and destination partitions.
|
# valid filesystems
|
||||||
# For example, if we want to copy XX partition, source partition will be
|
# shellcheck disable=2034
|
||||||
# /mnt/XX${SRCNUM}, and destination will be /mnt/XX${DSTNUM}
|
VALIDFS=(ext3 ext4 btrfs vfat reiserfs xfs zfs)
|
||||||
SRCNUM=2
|
|
||||||
DSTNUM=1
|
|
||||||
|
|
||||||
# array of partitions to copy
|
function man {
|
||||||
TO_COPY=(root export EFI)
|
sed -n '/^#%MAN_BEGIN%/,/^#%MAN_END%$/{//!s/^#[ ]\{0,1\}//p}' "$SCRIPT"
|
||||||
|
}
|
||||||
|
|
||||||
# An alternative to SRCNUM, DSTNUM, and TO_COPY variables would be to have
|
function usage {
|
||||||
# an array containing src and destination partitions:
|
cat <<_EOF
|
||||||
# (partsrc1 partdst1 partsrc2 partdst2 etc...)
|
Usage: $CMD [OPTIONS] [SRC] DST
|
||||||
# example:
|
Duplicate SRC (or live system) disk partitions to DST disk partitions.
|
||||||
# TO_COPY=(root2 root1 export2 export1)
|
|
||||||
# declare -i i
|
|
||||||
# for ((i=0; i<${#TO_COPY[@]}; i+=2)); do
|
|
||||||
# SRC=${#TO_COPY[$i]}
|
|
||||||
# DST=${#TO_COPY[$i + 1]}
|
|
||||||
# etc...
|
|
||||||
|
|
||||||
# where we will configure/install grub: mount point, device
|
Options:
|
||||||
GRUB_ROOT=/mnt/root${DSTNUM}
|
-a, --autofs=DIR autofs "LABEL-based" directory. Default is '/mnt'.
|
||||||
GRUB_DEV=/dev/$(lsblk -no pkname /dev/disk/by-label/root${DSTNUM})
|
-c, --copy=ACTION do partitions copies ('yes'), do not copy then ('no')
|
||||||
|
or ask for each of them ('ask'). Default is 'no'.
|
||||||
|
-d, --dry-run dry-run: nothing will be written to disk
|
||||||
|
-g, --grub install grub on destination disk
|
||||||
|
-h, --help this help
|
||||||
|
-m, --man display a "man-like" page and exit
|
||||||
|
--mariadb stop and restart mysql/mariadb server before and
|
||||||
|
after copies
|
||||||
|
-r, --root=PARTNUM root partition number on SRC device
|
||||||
|
mandatory if and only if SRC is provided
|
||||||
|
|
||||||
|
SRC and DST have strong constraints on partitions schemes and naming.
|
||||||
|
Type '$CMD --man" for more details"
|
||||||
|
_EOF
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# mariadb start/stop
|
||||||
|
function mariadb_maybe_stop {
|
||||||
|
if [[ $MARIADB == yes ]] && systemctl is-active --quiet mysql; then
|
||||||
|
#log -n "stopping mariadb/mysql... "
|
||||||
|
echorun systemctl stop mariadb
|
||||||
|
# bug if script stops here
|
||||||
|
MARIADBSTOPPED=yes
|
||||||
|
#log "done."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
function mariadb_maybe_start {
|
||||||
|
if [[ $MARIADB == yes && $MARIADBSTOPPED == yes ]]; then
|
||||||
|
#log -n "restarting mariadb/mysql... "
|
||||||
|
echorun systemctl start mariadb
|
||||||
|
MARIADBSTOPPED=no
|
||||||
|
#log "done."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# log function
|
||||||
|
# parameters:
|
||||||
|
# -l, -s: long, or short prefix (default: none). Last one is used.
|
||||||
|
# -t: timestamp
|
||||||
|
# -n: no newline
|
||||||
|
function log {
|
||||||
|
local timestr="" prefix="" opt=y newline=y
|
||||||
|
while [[ $opt = y ]]; do
|
||||||
|
case $1 in
|
||||||
|
-l) prefix=$(printf "*%.s" {1..30});;
|
||||||
|
-s) prefix=$(printf "*%.s" {1..5});;
|
||||||
|
-n) newline=n;;
|
||||||
|
-t) timestr=$(date "+%F %T%z - ");;
|
||||||
|
*) opt=n;;
|
||||||
|
esac
|
||||||
|
[[ $opt = y ]] && shift
|
||||||
|
done
|
||||||
|
[[ $prefix != "" ]] && printf "%s " "$prefix"
|
||||||
|
printf "%s" "$timestr"
|
||||||
|
# shellcheck disable=SC2059
|
||||||
|
printf "$@"
|
||||||
|
[[ $newline = y ]] && printf "\n"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# prints out and run a command.
|
||||||
|
function echorun {
|
||||||
|
if [[ "$DRYRUN" == 'yes' ]]; then
|
||||||
|
log "%s " "dry-run: " "$@"
|
||||||
|
else
|
||||||
|
log "%s " "$@"
|
||||||
|
"$@"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function error_handler {
|
||||||
|
local ERROR=$2
|
||||||
|
log "FATAL: Error line $1, exit code $2. Aborting."
|
||||||
|
exit "$ERROR"
|
||||||
|
}
|
||||||
|
trap 'error_handler $LINENO $?' ERR SIGHUP SIGINT SIGTERM
|
||||||
|
|
||||||
|
function exit_handler {
|
||||||
|
local mnt
|
||||||
|
|
||||||
|
# log "exit handler (at line $1)"
|
||||||
|
mariadb_maybe_start
|
||||||
|
if [[ -n "$DSTMNT" ]] && mountpoint -q "$DSTMNT"; then
|
||||||
|
for mnt in "$DSTMNT"/{dev,proc,sys}; do
|
||||||
|
if mountpoint -q "$mnt"; then
|
||||||
|
echorun umount "$mnt"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
trap 'exit_handler $LINENO' EXIT
|
||||||
|
|
||||||
|
function check_block_device {
|
||||||
|
local devtype="$1"
|
||||||
|
local mode="$2"
|
||||||
|
local dev="$3"
|
||||||
|
|
||||||
|
if [[ ! -b "$dev" ]]; then
|
||||||
|
log "$CMD: $devtype '$dev' is not a block device."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ ! -r "$dev" ]]; then
|
||||||
|
log "$CMD: $devtype '$dev' is not readable."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ $mode = "w" && ! -w "$dev" ]]; then
|
||||||
|
log "$CMD: $devtype '$dev' is not writable."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# check that /etc/fstab.XXX exists in SRCR
|
||||||
|
function check_fstab {
|
||||||
|
local fstab
|
||||||
|
for f in "$SRCROOTLABEL" "$DSTROOTLABEL"; do
|
||||||
|
fstab="${AUTOFS_DIR}/$SRCROOTLABEL/etc/fstab.$f"
|
||||||
|
#log -n "======= check %s=%s " "$fstab" "$f"
|
||||||
|
if [[ ! -f "$fstab" ]]; then
|
||||||
|
log "Fatal: source or destination fstab (%s) not found" "$fstab"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function fix_fstab {
|
||||||
|
local fstab="${AUTOFS_DIR}/$DSTROOTLABEL/etc/fstab"
|
||||||
|
echorun ln -f "$fstab.$DSTROOTLABEL" "$fstab"
|
||||||
|
}
|
||||||
|
|
||||||
|
# check if $1 is in array $2 ($2 is by reference)
|
||||||
|
function in_array {
|
||||||
|
local elt=$1 i
|
||||||
|
local -n arr=$2
|
||||||
|
for ((i=0; i<${#arr[@]}; ++i)); do
|
||||||
|
[[ ${arr[$i]} == "$elt" ]] && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# get y/n/q user input
|
||||||
|
function yesno {
|
||||||
|
local input
|
||||||
|
while true; do
|
||||||
|
printf "%s " "$1"
|
||||||
|
read -r input
|
||||||
|
case "$input" in
|
||||||
|
y|Y)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
q|Q)
|
||||||
|
log "aborting..."
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
n|N)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
printf "invalid answer. "
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# source and destination devices, root partition
|
||||||
|
SRC=""
|
||||||
|
DST=""
|
||||||
|
SRCROOT=""
|
||||||
|
ROOTPARTNUM=""
|
||||||
|
AUTOFS_DIR=/mnt
|
||||||
|
|
||||||
|
DRYRUN=no # dry-run
|
||||||
|
GRUB=no # install grub
|
||||||
|
COPY=no # do FS copies
|
||||||
|
MARIADB=no # stop/start mysql/mariadb
|
||||||
|
MARIADBSTOPPED=no # mysql stopped ?
|
||||||
|
|
||||||
|
# short and long options
|
||||||
|
SOPTS="a:c:dghmr:"
|
||||||
|
LOPTS="autofs:,copy:,dry-run,grub,help,man,mariadb,root:"
|
||||||
|
|
||||||
|
if ! TMP=$(getopt -o "$SOPTS" -l "$LOPTS" -n "$CMD" -- "$@"); then
|
||||||
|
log "Use '$CMD --help' or '$CMD --man' for help."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "$TMP"
|
||||||
|
unset TMP
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
'-a'|'--autofs')
|
||||||
|
AUTOFS_DIR="$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
'-c'|'--copy')
|
||||||
|
case "$2" in
|
||||||
|
"no") COPY=no;;
|
||||||
|
"yes") COPY=yes;;
|
||||||
|
"ask") COPY=ask;;
|
||||||
|
*) log "invalid '$2' --copy flag"
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
'-d'|'--dry-run')
|
||||||
|
DRYRUN=yes
|
||||||
|
;;
|
||||||
|
'-g'|'--grub')
|
||||||
|
GRUB=yes
|
||||||
|
;;
|
||||||
|
'-h'|'--help')
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
'-m'|'--man')
|
||||||
|
man
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
'--mariadb')
|
||||||
|
MARIADB=yes
|
||||||
|
;;
|
||||||
|
'-r'|'--root')
|
||||||
|
ROOTPARTNUM="$2"
|
||||||
|
if ! [[ "$ROOTPARTNUM" =~ ^[[:digit:]]+$ ]]; then
|
||||||
|
log "$CMD: $ROOTPARTNUM must be a partition number."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
'--')
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
log 'Internal error!'
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
case "$#" in
|
||||||
|
1)
|
||||||
|
if [[ -n "$ROOTPARTNUM" ]]; then
|
||||||
|
log "$CMD: cannot have --root option for live system."
|
||||||
|
log "Use '$CMD --help' or '$CMD --man' for help."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# guess root partition disk name
|
||||||
|
SRCROOT=$(findmnt -no SOURCE -M /)
|
||||||
|
ROOTPARTNUM=${SRCROOT: -1}
|
||||||
|
SRC="/dev/"$(lsblk -no pkname "$SRCROOT")
|
||||||
|
DST="/dev/$1"
|
||||||
|
;;
|
||||||
|
2)
|
||||||
|
if [[ -z "$ROOTPARTNUM" ]]; then
|
||||||
|
log "$CMD: missing --root option for non live system."
|
||||||
|
log "Use '$CMD --help' or '$CMD --man' for help."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
SRC="/dev/$1"
|
||||||
|
SRCROOT="$SRC$ROOTPARTNUM"
|
||||||
|
DST="/dev/$2"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
esac
|
||||||
|
|
||||||
|
# check SRC and DST are different, find out their characteristics
|
||||||
|
if [[ "$SRC" = "$DST" ]]; then
|
||||||
|
log "%s: Fatal: destination disk (%s) cannot be source.\n" "$CMD" "$SRC"
|
||||||
|
log "Use '%s --help' or '%s --man' for help." "$CMD" "$CMD"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
check_block_device "source disk" r "$SRC"
|
||||||
|
check_block_device "destination disk" w "$DST"
|
||||||
|
check_block_device "source root partition" r "$SRCROOT"
|
||||||
|
|
||||||
|
SRCROOTLABEL=$(lsblk -no label "$SRCROOT")
|
||||||
|
SRCCHAR=${SRCROOTLABEL: -1}
|
||||||
|
ROOTLABEL=${SRCROOTLABEL:0:-1}
|
||||||
|
|
||||||
|
# find out all partitions labels on SRC disk...
|
||||||
|
# shellcheck disable=SC2207
|
||||||
|
declare -a SRCLABELS=($(lsblk -lno LABEL "$SRC"))
|
||||||
|
# shellcheck disable=SC2206
|
||||||
|
declare -a LABELS=(${SRCLABELS[@]%?})
|
||||||
|
|
||||||
|
# ... and corresponding partition device and fstype
|
||||||
|
declare -a SRCDEVS SRCFS SRC_VALID_FS
|
||||||
|
for ((i=0; i<${#LABELS[@]}; ++i)); do
|
||||||
|
TMP="${LABELS[$i]}$SRCCHAR"
|
||||||
|
TMP="${SRCLABELS[$i]}"
|
||||||
|
TMPDEV=$(findfs LABEL="$TMP")
|
||||||
|
TMPFS=$(lsblk -no fstype "$TMPDEV")
|
||||||
|
log "found LABEL=$TMP DEV=$TMPDEV FSTYPE=$TMPFS"
|
||||||
|
SRCDEVS[$i]="$TMPDEV"
|
||||||
|
SRCFS[$i]="$TMPFS"
|
||||||
|
SRC_VALID_FS[$i]=n
|
||||||
|
in_array "$TMPFS" VALIDFS && SRC_VALID_FS[$i]=y
|
||||||
|
unset TMP TMPDEV TMPFS
|
||||||
|
done
|
||||||
|
|
||||||
|
DSTROOT="$DST$ROOTPARTNUM"
|
||||||
|
check_block_device "destination root partition" w "$DSTROOT"
|
||||||
|
DSTROOTLABEL=$(lsblk -no label "$DSTROOT")
|
||||||
|
DSTCHAR=${DSTROOTLABEL: -1}
|
||||||
|
|
||||||
|
# check DSTROOTLABEL is compatible with ROOTLABEL
|
||||||
|
if [[ "$DSTROOTLABEL" != "$ROOTLABEL$DSTCHAR" ]]; then
|
||||||
|
log "%s: Fatal: %s != %s%s." "$CMD" "$DSTROOTLABEL" "$ROOTLABEL" "$DSTCHAR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
declare -a DSTLABELS DSTDEVS DSTFS DST_VALID_FS
|
||||||
|
# Do the same for corresponding DST partitions labels, device, and fstype
|
||||||
|
for ((i=0; i<${#LABELS[@]}; ++i)); do
|
||||||
|
TMP="${LABELS[$i]}$DSTCHAR"
|
||||||
|
log -n "Looking for [%s] label... " "$TMP"
|
||||||
|
if ! TMPDEV=$(findfs LABEL="$TMP"); then
|
||||||
|
log "not found."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
TMPDISK=${TMPDEV%?}
|
||||||
|
log -n "DEV=%s... DISK=%s..." "$TMPDEV" "$TMPDISK"
|
||||||
|
if [[ "$TMPDISK" != "$DST" ]]; then
|
||||||
|
log "wrong disk (%s != %s)" "$TMPDISK" "$DST"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
TMPFS=$(lsblk -no fstype "$TMPDEV")
|
||||||
|
log "FSTYPE=%s" "$TMPFS"
|
||||||
|
DSTLABELS[$i]="$TMP"
|
||||||
|
DSTDEVS[$i]="$TMPDEV"
|
||||||
|
DSTFS[$i]="$TMPFS"
|
||||||
|
DST_VALID_FS[$i]=n
|
||||||
|
in_array "$TMPFS" VALIDFS && DST_VALID_FS[$i]=y
|
||||||
|
unset TMP TMPDEV TMPFS
|
||||||
|
done
|
||||||
|
|
||||||
|
for ((i=0; i<${#LABELS[@]}; ++i)); do
|
||||||
|
log -n "%s %s " "${SRCDEVS[$i]}" "${DSTDEVS[$i]}"
|
||||||
|
log -n "%s %s " "${SRCLABELS[$i]}" "${DSTLABELS[$i]}"
|
||||||
|
log -n "%s %s " "${SRCFS[$i]}" "${DSTFS[$i]}"
|
||||||
|
log -n "%s %s " "${SRC_VALID_FS[$i]}" "${DST_VALID_FS[$i]}"
|
||||||
|
[[ "$DSTROOTLABEL" == "${DSTLABELS[$i]}" ]] && log "*"
|
||||||
|
echo
|
||||||
|
done | column -N DEV1,DEV2,LABEL1,LABEL2,FS1,FS2,SVALID\?,DVALID\?,ROOT -t -o " | "
|
||||||
|
|
||||||
|
check_fstab || exit 1
|
||||||
|
|
||||||
# we will use ".rsync-disk-copy" files to exclude files/dirs
|
|
||||||
RSYNCOPTS="-axH --delete --delete-excluded"
|
RSYNCOPTS="-axH --delete --delete-excluded"
|
||||||
FILTER=--filter="dir-merge .rsync-disk-copy"
|
FILTER=--filter="dir-merge .rsync-disk-copy"
|
||||||
|
# copy loop
|
||||||
|
for ((i=0; i<${#LABELS[@]}; ++i)); do
|
||||||
|
if [[ "${SRC_VALID_FS[$i]}" != y ]] || [[ "${DST_VALID_FS[$i]}" != y ]]; then
|
||||||
|
log "skipping label %s" "${LABELS[$i]}"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
SRCPART="${AUTOFS_DIR}/${SRCLABELS[$i]}/"
|
||||||
|
DSTPART="$AUTOFS_DIR/${DSTLABELS[$i]}"
|
||||||
|
|
||||||
# stop what could be problematic (databases, etc...)
|
#log -n "%s -> %s : " "$SRCPART" "$DSTPART"
|
||||||
systemctl stop mysql
|
#log "\t%s %s %s %s %s" rsync "${RSYNCOPTS}" "$FILTER" "$SRCPART" "$DSTPART"
|
||||||
|
copy="$COPY"
|
||||||
# partitions copy
|
if [[ "$COPY" == 'ask' ]]; then
|
||||||
for part in ${TO_COPY[@]}; do
|
yesno "Copy $SRCPART to $DSTPART ? [y/n/q]" && copy=yes || copy=no
|
||||||
SRCPART=/mnt/${part}${SRCNUM}/
|
fi
|
||||||
DSTPART=/mnt/${part}${DSTNUM}
|
if [[ "$copy" == yes ]]; then
|
||||||
|
mariadb_maybe_stop
|
||||||
echo copy from $SRCPART to $DSTPART
|
# shellcheck disable=SC2086
|
||||||
echo -n "press a key to continue..."
|
echorun rsync "$FILTER" ${RSYNCOPTS} "$SRCPART" "$DSTPART"
|
||||||
read -r key
|
if [[ "$DSTROOTLABEL" == "${DSTLABELS[$i]}" ]]; then
|
||||||
echo rsync ${RSYNCOPTS} "$FILTER" "$SRCPART" "$DSTPART"
|
fix_fstab
|
||||||
rsync ${RSYNCOPTS} "$FILTER" "$SRCPART" "$DSTPART"
|
fi
|
||||||
|
fi
|
||||||
|
#log ""
|
||||||
done
|
done
|
||||||
|
|
||||||
# grub install
|
# grub install
|
||||||
|
if [[ "$GRUB" == yes ]]; then
|
||||||
|
log "installing grub on $DST..."
|
||||||
|
DSTMNT="$AUTOFS_DIR/$DSTROOTLABEL"
|
||||||
# mount virtual devices
|
# mount virtual devices
|
||||||
mount -o bind /sys ${GRUB_ROOT}/sys
|
echorun mount -o bind /sys "$DSTMNT/sys"
|
||||||
mount -o bind /proc ${GRUB_ROOT}/proc
|
echorun mount -o bind /proc "$DSTMNT/proc"
|
||||||
mount -o bind /dev ${GRUB_ROOT}/dev
|
echorun mount -o bind /dev "$DSTMNT/dev"
|
||||||
|
|
||||||
chroot ${GRUB_ROOT} update-grub
|
echorun chroot "$DSTMNT" update-grub
|
||||||
chroot ${GRUB_ROOT} grub-install ${GRUB_DEV}
|
echorun chroot "$DSTMNT" grub-install "$DST"
|
||||||
|
fi
|
||||||
|
|
||||||
# restart stopped process (db, etc...)
|
exit 0
|
||||||
systemctl start mysql
|
|
||||||
|
Reference in New Issue
Block a user