Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
77d634710e | |||
5c5181a252 | |||
67042466e8 | |||
757d0ccc76 | |||
4a55fa87d5 | |||
032cd1b862 | |||
ec2434fe3c | |||
34389132d9 | |||
78ee20d72d | |||
601e42ac35 | |||
e1897b363b |
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "proxmox-backup"
|
||||
version = "0.8.6"
|
||||
version = "0.8.7"
|
||||
authors = ["Dietmar Maurer <dietmar@proxmox.com>"]
|
||||
edition = "2018"
|
||||
license = "AGPL-3"
|
||||
|
10
debian/changelog
vendored
10
debian/changelog
vendored
@ -1,3 +1,13 @@
|
||||
rust-proxmox-backup (0.8.7-2) unstable; urgency=medium
|
||||
|
||||
* support restoring file attributes from pxar archives
|
||||
|
||||
* docs: additions and fixes
|
||||
|
||||
* ui: running tasks: update limit to 100
|
||||
|
||||
-- Proxmox Support Team <support@proxmox.com> Tue, 14 Jul 2020 12:05:25 +0200
|
||||
|
||||
rust-proxmox-backup (0.8.6-1) unstable; urgency=medium
|
||||
|
||||
* ui: add button for easily showing the server fingerprint dashboard
|
||||
|
1
debian/lintian-overrides
vendored
1
debian/lintian-overrides
vendored
@ -1 +1,2 @@
|
||||
proxmox-backup-server: package-installs-apt-sources etc/apt/sources.list.d/pbstest-beta.list
|
||||
proxmox-backup-server: systemd-service-file-refers-to-unusual-wantedby-target lib/systemd/system/proxmox-backup-banner.service getty.target
|
||||
|
@ -541,7 +541,7 @@ environment variable ``PBS_REPOSITORY``.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# export PBS_REPOSTORY=backup-server:store1
|
||||
# export PBS_REPOSITORY=backup-server:store1
|
||||
|
||||
After this you can execute all commands without specifying the ``--repository``
|
||||
option.
|
||||
|
@ -83,6 +83,10 @@ In general this is not trivial, especially when LVM_ or ZFS_ is used.
|
||||
|
||||
The network configuration is completely up to you as well.
|
||||
|
||||
.. note:: You can access the webinterface of the Proxmox Backup Server with
|
||||
your web browser, using HTTPS on port 8007. For example at
|
||||
``https://<ip-or-dns-name>:8007``
|
||||
|
||||
Install Proxmox Backup server on `Proxmox VE`_
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@ -99,6 +103,10 @@ After configuring the
|
||||
server to store backups. Should the hypervisor server fail, you can
|
||||
still access the backups.
|
||||
|
||||
.. note:: You can access the webinterface of the Proxmox Backup Server with
|
||||
your web browser, using HTTPS on port 8007. For example at
|
||||
``https://<ip-or-dns-name>:8007``
|
||||
|
||||
Client installation
|
||||
-------------------
|
||||
|
||||
|
@ -33,6 +33,46 @@ During the Proxmox Backup beta phase only one repository (pbstest) will be
|
||||
available. Once released, a Enterprise repository for production use and a
|
||||
no-subscription repository will be provided.
|
||||
|
||||
SecureApt
|
||||
~~~~~~~~~
|
||||
|
||||
The `Release` files in the repositories are signed with GnuPG. APT is using
|
||||
these signatures to verify that all packages are from a trusted source.
|
||||
|
||||
If you install Proxmox Backup Server from an official ISO image, the key for
|
||||
verification is already installed.
|
||||
|
||||
If you install Proxmox Backup Server on top of Debian, download and install the
|
||||
key with the following commands:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# wget http://download.proxmox.com/debian/proxmox-ve-release-6.x.gpg -O /etc/apt/trusted.gpg.d/proxmox-ve-release-6.x.gpg
|
||||
|
||||
Verify the SHA512 checksum afterwards with:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# sha512sum /etc/apt/trusted.gpg.d/proxmox-ve-release-6.x.gpg
|
||||
|
||||
The output should be:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
acca6f416917e8e11490a08a1e2842d500b3a5d9f322c6319db0927b2901c3eae23cfb5cd5df6facf2b57399d3cfa52ad7769ebdd75d9b204549ca147da52626 /etc/apt/trusted.gpg.d/proxmox-ve-release-6.x.gpg
|
||||
|
||||
and the md5sum:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
# md5sum /etc/apt/trusted.gpg.d/proxmox-ve-release-6.x.gpg
|
||||
|
||||
Here, the output should be:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
f3f6c5a3a67baf38ad178e5ff1ee270c /etc/apt/trusted.gpg.d/proxmox-ve-release-6.x.gpg
|
||||
|
||||
.. comment
|
||||
`Proxmox Backup`_ Enterprise Repository
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -23,6 +23,7 @@ use proxmox::tools::fd::RawFdNum;
|
||||
use proxmox::tools::vec;
|
||||
|
||||
use crate::pxar::catalog::BackupCatalogWriter;
|
||||
use crate::pxar::metadata::errno_is_unsupported;
|
||||
use crate::pxar::Flags;
|
||||
use crate::pxar::tools::assert_single_path_component;
|
||||
use crate::tools::{acl, fs, xattr, Fd};
|
||||
@ -698,13 +699,6 @@ fn get_metadata(fd: RawFd, stat: &FileStat, flags: Flags, fs_magic: i64) -> Resu
|
||||
Ok(meta)
|
||||
}
|
||||
|
||||
fn errno_is_unsupported(errno: Errno) -> bool {
|
||||
match errno {
|
||||
Errno::ENOTTY | Errno::ENOSYS | Errno::EBADF | Errno::EOPNOTSUPP | Errno::EINVAL => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_fcaps(meta: &mut Metadata, fd: RawFd, flags: Flags) -> Result<(), Error> {
|
||||
if flags.contains(Flags::WITH_FCAPS) {
|
||||
return Ok(());
|
||||
@ -769,7 +763,7 @@ fn get_xattr_fcaps_acl(
|
||||
}
|
||||
|
||||
fn get_chattr(metadata: &mut Metadata, fd: RawFd) -> Result<(), Error> {
|
||||
let mut attr: usize = 0;
|
||||
let mut attr: libc::c_long = 0;
|
||||
|
||||
match unsafe { fs::read_attr_fd(fd, &mut attr) } {
|
||||
Ok(_) => (),
|
||||
@ -779,7 +773,7 @@ fn get_chattr(metadata: &mut Metadata, fd: RawFd) -> Result<(), Error> {
|
||||
Err(err) => bail!("failed to read file attributes: {}", err),
|
||||
}
|
||||
|
||||
metadata.stat.flags |= Flags::from_chattr(attr as u32).bits();
|
||||
metadata.stat.flags |= Flags::from_chattr(attr).bits();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -230,7 +230,8 @@ impl Extractor {
|
||||
dir.metadata(),
|
||||
fd,
|
||||
&CString::new(dir.file_name().as_bytes())?,
|
||||
)?;
|
||||
)
|
||||
.map_err(|err| format_err!("failed to apply directory metadata: {}", err))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -241,7 +242,9 @@ impl Extractor {
|
||||
}
|
||||
|
||||
fn parent_fd(&mut self) -> Result<RawFd, Error> {
|
||||
self.dir_stack.last_dir_fd(self.allow_existing_dirs)
|
||||
self.dir_stack
|
||||
.last_dir_fd(self.allow_existing_dirs)
|
||||
.map_err(|err| format_err!("failed to get parent directory file descriptor: {}", err))
|
||||
}
|
||||
|
||||
pub fn extract_symlink(
|
||||
@ -320,10 +323,14 @@ impl Extractor {
|
||||
file_name,
|
||||
OFlag::O_CREAT | OFlag::O_EXCL | OFlag::O_WRONLY | OFlag::O_CLOEXEC,
|
||||
Mode::from_bits(0o600).unwrap(),
|
||||
)?)
|
||||
)
|
||||
.map_err(|err| format_err!("failed to create file {:?}: {}", file_name, err))?)
|
||||
};
|
||||
|
||||
let extracted = io::copy(&mut *contents, &mut file)?;
|
||||
metadata::apply_initial_flags(self.feature_flags, metadata, file.as_raw_fd())?;
|
||||
|
||||
let extracted = io::copy(&mut *contents, &mut file)
|
||||
.map_err(|err| format_err!("failed to copy file contents: {}", err))?;
|
||||
if size != extracted {
|
||||
bail!("extracted {} bytes of a file of {} bytes", extracted, size);
|
||||
}
|
||||
@ -345,10 +352,15 @@ impl Extractor {
|
||||
file_name,
|
||||
OFlag::O_CREAT | OFlag::O_EXCL | OFlag::O_WRONLY | OFlag::O_CLOEXEC,
|
||||
Mode::from_bits(0o600).unwrap(),
|
||||
)?)
|
||||
)
|
||||
.map_err(|err| format_err!("failed to create file {:?}: {}", file_name, err))?)
|
||||
});
|
||||
|
||||
let extracted = tokio::io::copy(&mut *contents, &mut file).await?;
|
||||
metadata::apply_initial_flags(self.feature_flags, metadata, file.as_raw_fd())?;
|
||||
|
||||
let extracted = tokio::io::copy(&mut *contents, &mut file)
|
||||
.await
|
||||
.map_err(|err| format_err!("failed to copy file contents: {}", err))?;
|
||||
if size != extracted {
|
||||
bail!("extracted {} bytes of a file of {} bytes", extracted, size);
|
||||
}
|
||||
|
@ -3,6 +3,8 @@
|
||||
//! Flags for known supported features for a given filesystem can be derived
|
||||
//! from the superblocks magic number.
|
||||
|
||||
use libc::c_long;
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
bitflags! {
|
||||
@ -149,34 +151,54 @@ impl Default for Flags {
|
||||
}
|
||||
}
|
||||
|
||||
// form /usr/include/linux/fs.h
|
||||
const FS_APPEND_FL: c_long = 0x0000_0020;
|
||||
const FS_NOATIME_FL: c_long = 0x0000_0080;
|
||||
const FS_COMPR_FL: c_long = 0x0000_0004;
|
||||
const FS_NOCOW_FL: c_long = 0x0080_0000;
|
||||
const FS_NODUMP_FL: c_long = 0x0000_0040;
|
||||
const FS_DIRSYNC_FL: c_long = 0x0001_0000;
|
||||
const FS_IMMUTABLE_FL: c_long = 0x0000_0010;
|
||||
const FS_SYNC_FL: c_long = 0x0000_0008;
|
||||
const FS_NOCOMP_FL: c_long = 0x0000_0400;
|
||||
const FS_PROJINHERIT_FL: c_long = 0x2000_0000;
|
||||
|
||||
pub(crate) const INITIAL_FS_FLAGS: c_long =
|
||||
FS_NOATIME_FL
|
||||
| FS_COMPR_FL
|
||||
| FS_NOCOW_FL
|
||||
| FS_NOCOMP_FL
|
||||
| FS_PROJINHERIT_FL;
|
||||
|
||||
#[rustfmt::skip]
|
||||
const CHATTR_MAP: [(Flags, c_long); 10] = [
|
||||
( Flags::WITH_FLAG_APPEND, FS_APPEND_FL ),
|
||||
( Flags::WITH_FLAG_NOATIME, FS_NOATIME_FL ),
|
||||
( Flags::WITH_FLAG_COMPR, FS_COMPR_FL ),
|
||||
( Flags::WITH_FLAG_NOCOW, FS_NOCOW_FL ),
|
||||
( Flags::WITH_FLAG_NODUMP, FS_NODUMP_FL ),
|
||||
( Flags::WITH_FLAG_DIRSYNC, FS_DIRSYNC_FL ),
|
||||
( Flags::WITH_FLAG_IMMUTABLE, FS_IMMUTABLE_FL ),
|
||||
( Flags::WITH_FLAG_SYNC, FS_SYNC_FL ),
|
||||
( Flags::WITH_FLAG_NOCOMP, FS_NOCOMP_FL ),
|
||||
( Flags::WITH_FLAG_PROJINHERIT, FS_PROJINHERIT_FL ),
|
||||
];
|
||||
|
||||
// from /usr/include/linux/msdos_fs.h
|
||||
const ATTR_HIDDEN: u32 = 2;
|
||||
const ATTR_SYS: u32 = 4;
|
||||
const ATTR_ARCH: u32 = 32;
|
||||
|
||||
#[rustfmt::skip]
|
||||
const FAT_ATTR_MAP: [(Flags, u32); 3] = [
|
||||
( Flags::WITH_FLAG_HIDDEN, ATTR_HIDDEN ),
|
||||
( Flags::WITH_FLAG_SYSTEM, ATTR_SYS ),
|
||||
( Flags::WITH_FLAG_ARCHIVE, ATTR_ARCH ),
|
||||
];
|
||||
|
||||
impl Flags {
|
||||
/// Get a set of feature flags from file attributes.
|
||||
pub fn from_chattr(attr: u32) -> Flags {
|
||||
// form /usr/include/linux/fs.h
|
||||
const FS_APPEND_FL: u32 = 0x0000_0020;
|
||||
const FS_NOATIME_FL: u32 = 0x0000_0080;
|
||||
const FS_COMPR_FL: u32 = 0x0000_0004;
|
||||
const FS_NOCOW_FL: u32 = 0x0080_0000;
|
||||
const FS_NODUMP_FL: u32 = 0x0000_0040;
|
||||
const FS_DIRSYNC_FL: u32 = 0x0001_0000;
|
||||
const FS_IMMUTABLE_FL: u32 = 0x0000_0010;
|
||||
const FS_SYNC_FL: u32 = 0x0000_0008;
|
||||
const FS_NOCOMP_FL: u32 = 0x0000_0400;
|
||||
const FS_PROJINHERIT_FL: u32 = 0x2000_0000;
|
||||
|
||||
const CHATTR_MAP: [(Flags, u32); 10] = [
|
||||
( Flags::WITH_FLAG_APPEND, FS_APPEND_FL ),
|
||||
( Flags::WITH_FLAG_NOATIME, FS_NOATIME_FL ),
|
||||
( Flags::WITH_FLAG_COMPR, FS_COMPR_FL ),
|
||||
( Flags::WITH_FLAG_NOCOW, FS_NOCOW_FL ),
|
||||
( Flags::WITH_FLAG_NODUMP, FS_NODUMP_FL ),
|
||||
( Flags::WITH_FLAG_DIRSYNC, FS_DIRSYNC_FL ),
|
||||
( Flags::WITH_FLAG_IMMUTABLE, FS_IMMUTABLE_FL ),
|
||||
( Flags::WITH_FLAG_SYNC, FS_SYNC_FL ),
|
||||
( Flags::WITH_FLAG_NOCOMP, FS_NOCOMP_FL ),
|
||||
( Flags::WITH_FLAG_PROJINHERIT, FS_PROJINHERIT_FL ),
|
||||
];
|
||||
|
||||
pub fn from_chattr(attr: c_long) -> Flags {
|
||||
let mut flags = Flags::empty();
|
||||
|
||||
for (fe_flag, fs_flag) in &CHATTR_MAP {
|
||||
@ -188,19 +210,25 @@ impl Flags {
|
||||
flags
|
||||
}
|
||||
|
||||
/// Get the chattr bit representation of these feature flags.
|
||||
pub fn to_chattr(self) -> c_long {
|
||||
let mut flags: c_long = 0;
|
||||
|
||||
for (fe_flag, fs_flag) in &CHATTR_MAP {
|
||||
if self.contains(*fe_flag) {
|
||||
flags |= *fs_flag;
|
||||
}
|
||||
}
|
||||
|
||||
flags
|
||||
}
|
||||
|
||||
pub fn to_initial_chattr(self) -> c_long {
|
||||
self.to_chattr() & INITIAL_FS_FLAGS
|
||||
}
|
||||
|
||||
/// Get a set of feature flags from FAT attributes.
|
||||
pub fn from_fat_attr(attr: u32) -> Flags {
|
||||
// from /usr/include/linux/msdos_fs.h
|
||||
const ATTR_HIDDEN: u32 = 2;
|
||||
const ATTR_SYS: u32 = 4;
|
||||
const ATTR_ARCH: u32 = 32;
|
||||
|
||||
const FAT_ATTR_MAP: [(Flags, u32); 3] = [
|
||||
( Flags::WITH_FLAG_HIDDEN, ATTR_HIDDEN ),
|
||||
( Flags::WITH_FLAG_SYSTEM, ATTR_SYS ),
|
||||
( Flags::WITH_FLAG_ARCHIVE, ATTR_ARCH ),
|
||||
];
|
||||
|
||||
let mut flags = Flags::empty();
|
||||
|
||||
for (fe_flag, fs_flag) in &FAT_ATTR_MAP {
|
||||
@ -212,6 +240,19 @@ impl Flags {
|
||||
flags
|
||||
}
|
||||
|
||||
/// Get the fat attribute bit representation of these feature flags.
|
||||
pub fn to_fat_attr(self) -> u32 {
|
||||
let mut flags = 0u32;
|
||||
|
||||
for (fe_flag, fs_flag) in &FAT_ATTR_MAP {
|
||||
if self.contains(*fe_flag) {
|
||||
flags |= *fs_flag;
|
||||
}
|
||||
}
|
||||
|
||||
flags
|
||||
}
|
||||
|
||||
/// Return the supported *pxar* feature flags based on the magic number of the filesystem.
|
||||
pub fn from_magic(magic: i64) -> Flags {
|
||||
use proxmox::sys::linux::magic::*;
|
||||
|
@ -79,13 +79,19 @@ pub fn apply_at(
|
||||
apply(flags, metadata, fd.as_raw_fd(), file_name)
|
||||
}
|
||||
|
||||
pub fn apply_initial_flags(
|
||||
flags: Flags,
|
||||
metadata: &Metadata,
|
||||
fd: RawFd,
|
||||
) -> Result<(), Error> {
|
||||
let entry_flags = Flags::from_bits_truncate(metadata.stat.flags);
|
||||
apply_chattr(fd, entry_flags.to_initial_chattr(), flags.to_initial_chattr())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn apply(flags: Flags, metadata: &Metadata, fd: RawFd, file_name: &CStr) -> Result<(), Error> {
|
||||
let c_proc_path = CString::new(format!("/proc/self/fd/{}", fd)).unwrap();
|
||||
|
||||
if metadata.stat.flags != 0 {
|
||||
todo!("apply flags!");
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// UID and GID first, as this fails if we lose access anyway.
|
||||
c_result!(libc::chown(
|
||||
@ -94,13 +100,15 @@ pub fn apply(flags: Flags, metadata: &Metadata, fd: RawFd, file_name: &CStr) ->
|
||||
metadata.stat.gid
|
||||
))
|
||||
.map(drop)
|
||||
.or_else(allow_notsupp)?;
|
||||
.or_else(allow_notsupp)
|
||||
.map_err(|err| format_err!("failed to set ownership: {}", err))?;
|
||||
}
|
||||
|
||||
let mut skip_xattrs = false;
|
||||
apply_xattrs(flags, c_proc_path.as_ptr(), metadata, &mut skip_xattrs)?;
|
||||
add_fcaps(flags, c_proc_path.as_ptr(), metadata, &mut skip_xattrs)?;
|
||||
apply_acls(flags, &c_proc_path, metadata)?;
|
||||
apply_acls(flags, &c_proc_path, metadata)
|
||||
.map_err(|err| format_err!("failed to apply acls: {}", err))?;
|
||||
apply_quota_project_id(flags, fd, metadata)?;
|
||||
|
||||
// Finally mode and time. We may lose access with mode, but the changing the mode also
|
||||
@ -110,7 +118,12 @@ pub fn apply(flags: Flags, metadata: &Metadata, fd: RawFd, file_name: &CStr) ->
|
||||
libc::chmod(c_proc_path.as_ptr(), perms_from_metadata(metadata)?.bits())
|
||||
})
|
||||
.map(drop)
|
||||
.or_else(allow_notsupp)?;
|
||||
.or_else(allow_notsupp)
|
||||
.map_err(|err| format_err!("failed to change file mode: {}", err))?;
|
||||
}
|
||||
|
||||
if metadata.stat.flags != 0 {
|
||||
apply_flags(flags, fd, metadata.stat.flags)?;
|
||||
}
|
||||
|
||||
let res = c_result!(unsafe {
|
||||
@ -160,7 +173,8 @@ fn add_fcaps(
|
||||
)
|
||||
})
|
||||
.map(drop)
|
||||
.or_else(|err| allow_notsupp_remember(err, skip_xattrs))?;
|
||||
.or_else(|err| allow_notsupp_remember(err, skip_xattrs))
|
||||
.map_err(|err| format_err!("failed to apply file capabilities: {}", err))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -195,7 +209,8 @@ fn apply_xattrs(
|
||||
)
|
||||
})
|
||||
.map(drop)
|
||||
.or_else(|err| allow_notsupp_remember(err, &mut *skip_xattrs))?;
|
||||
.or_else(|err| allow_notsupp_remember(err, &mut *skip_xattrs))
|
||||
.map_err(|err| format_err!("failed to apply extended attributes: {}", err))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -317,3 +332,49 @@ fn apply_quota_project_id(flags: Flags, fd: RawFd, metadata: &Metadata) -> Resul
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn errno_is_unsupported(errno: Errno) -> bool {
|
||||
match errno {
|
||||
Errno::ENOTTY | Errno::ENOSYS | Errno::EBADF | Errno::EOPNOTSUPP | Errno::EINVAL => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_chattr(fd: RawFd, chattr: libc::c_long, mask: libc::c_long) -> Result<(), Error> {
|
||||
if chattr == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut fattr: libc::c_long = 0;
|
||||
match unsafe { fs::read_attr_fd(fd, &mut fattr) } {
|
||||
Ok(_) => (),
|
||||
Err(nix::Error::Sys(errno)) if errno_is_unsupported(errno) => {
|
||||
return Ok(());
|
||||
}
|
||||
Err(err) => bail!("failed to read file attributes: {}", err),
|
||||
}
|
||||
|
||||
let attr = (chattr & mask) | (fattr & !mask);
|
||||
match unsafe { fs::write_attr_fd(fd, &attr) } {
|
||||
Ok(_) => Ok(()),
|
||||
Err(nix::Error::Sys(errno)) if errno_is_unsupported(errno) => Ok(()),
|
||||
Err(err) => bail!("failed to set file attributes: {}", err),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_flags(flags: Flags, fd: RawFd, entry_flags: u64) -> Result<(), Error> {
|
||||
let entry_flags = Flags::from_bits_truncate(entry_flags);
|
||||
|
||||
apply_chattr(fd, entry_flags.to_chattr(), flags.to_chattr())?;
|
||||
|
||||
let fatattr = (flags & entry_flags).to_fat_attr();
|
||||
if fatattr != 0 {
|
||||
match unsafe { fs::write_fat_attr_fd(fd, &fatattr) } {
|
||||
Ok(_) => (),
|
||||
Err(nix::Error::Sys(errno)) if errno_is_unsupported(errno) => (),
|
||||
Err(err) => bail!("failed to set file attributes: {}", err),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -222,11 +222,13 @@ where
|
||||
|
||||
// /usr/include/linux/fs.h: #define FS_IOC_GETFLAGS _IOR('f', 1, long)
|
||||
// read Linux file system attributes (see man chattr)
|
||||
nix::ioctl_read!(read_attr_fd, b'f', 1, usize);
|
||||
nix::ioctl_read!(read_attr_fd, b'f', 1, libc::c_long);
|
||||
nix::ioctl_write_ptr!(write_attr_fd, b'f', 2, libc::c_long);
|
||||
|
||||
// /usr/include/linux/msdos_fs.h: #define FAT_IOCTL_GET_ATTRIBUTES _IOR('r', 0x10, __u32)
|
||||
// read FAT file system attributes
|
||||
nix::ioctl_read!(read_fat_attr_fd, b'r', 0x10, u32);
|
||||
nix::ioctl_write_ptr!(write_fat_attr_fd, b'r', 0x11, u32);
|
||||
|
||||
// From /usr/include/linux/fs.h
|
||||
// #define FS_IOC_FSGETXATTR _IOR('X', 31, struct fsxattr)
|
||||
|
@ -55,6 +55,10 @@ js/proxmox-backup-gui.js: js OnlineHelpInfo.js ${JSSRC}
|
||||
cat OnlineHelpInfo.js ${JSSRC} >$@.tmp
|
||||
mv $@.tmp $@
|
||||
|
||||
.PHONY: lint
|
||||
lint: ${JSSRC}
|
||||
eslint ${JSSRC}
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
find . -name '*~' -exec rm {} ';'
|
||||
|
@ -13,7 +13,7 @@ Ext.define('PBS.data.RunningTasksStore', {
|
||||
proxy: {
|
||||
type: 'proxmox',
|
||||
// maybe separate api call?
|
||||
url: '/api2/json/nodes/localhost/tasks?running=1',
|
||||
url: '/api2/json/nodes/localhost/tasks?running=1&limit=100',
|
||||
},
|
||||
});
|
||||
me.callParent([config]);
|
||||
|
@ -21,134 +21,128 @@ Ext.define('PBS.DataStoreEdit', {
|
||||
return {};
|
||||
},
|
||||
|
||||
items: [
|
||||
{
|
||||
xtype: 'tabpanel',
|
||||
bodyPadding: 10,
|
||||
items: [
|
||||
{
|
||||
title: gettext('General'),
|
||||
xtype: 'inputpanel',
|
||||
column1: [
|
||||
{
|
||||
xtype: 'pmxDisplayEditField',
|
||||
cbind: {
|
||||
editable: '{isCreate}',
|
||||
},
|
||||
name: 'name',
|
||||
allowBlank: false,
|
||||
fieldLabel: gettext('Name'),
|
||||
items: {
|
||||
xtype: 'tabpanel',
|
||||
bodyPadding: 10,
|
||||
items: [
|
||||
{
|
||||
title: gettext('General'),
|
||||
xtype: 'inputpanel',
|
||||
column1: [
|
||||
{
|
||||
xtype: 'pmxDisplayEditField',
|
||||
cbind: {
|
||||
editable: '{isCreate}',
|
||||
},
|
||||
{
|
||||
xtype: 'pmxDisplayEditField',
|
||||
cbind: {
|
||||
editable: '{isCreate}',
|
||||
},
|
||||
name: 'path',
|
||||
allowBlank: false,
|
||||
fieldLabel: gettext('Backing Path'),
|
||||
emptyText: gettext('An absolute path'),
|
||||
name: 'name',
|
||||
allowBlank: false,
|
||||
fieldLabel: gettext('Name'),
|
||||
},
|
||||
{
|
||||
xtype: 'pmxDisplayEditField',
|
||||
cbind: {
|
||||
editable: '{isCreate}',
|
||||
},
|
||||
],
|
||||
|
||||
column2: [
|
||||
{
|
||||
xtype: 'proxmoxtextfield',
|
||||
name: 'gc-schedule',
|
||||
fieldLabel: gettext("GC Schedule"),
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
name: 'path',
|
||||
allowBlank: false,
|
||||
fieldLabel: gettext('Backing Path'),
|
||||
emptyText: gettext('An absolute path'),
|
||||
},
|
||||
],
|
||||
column2: [
|
||||
{
|
||||
xtype: 'proxmoxtextfield',
|
||||
name: 'gc-schedule',
|
||||
fieldLabel: gettext("GC Schedule"),
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
{
|
||||
xtype: 'proxmoxtextfield',
|
||||
name: 'prune-schedule',
|
||||
fieldLabel: gettext("Prune Schedule"),
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
},
|
||||
{
|
||||
xtype: 'proxmoxtextfield',
|
||||
name: 'prune-schedule',
|
||||
fieldLabel: gettext("Prune Schedule"),
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
],
|
||||
|
||||
columnB: [
|
||||
{
|
||||
xtype: 'textfield',
|
||||
name: 'comment',
|
||||
fieldLabel: gettext('Comment'),
|
||||
},
|
||||
],
|
||||
columnB: [
|
||||
{
|
||||
xtype: 'textfield',
|
||||
name: 'comment',
|
||||
fieldLabel: gettext('Comment'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: gettext('Prune Options'),
|
||||
xtype: 'inputpanel',
|
||||
column1: [
|
||||
{
|
||||
xtype: 'proxmoxintegerfield',
|
||||
fieldLabel: gettext('Keep Last'),
|
||||
name: 'keep-last',
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: gettext('Prune Options'),
|
||||
xtype: 'inputpanel',
|
||||
column1: [
|
||||
{
|
||||
xtype: 'proxmoxintegerfield',
|
||||
fieldLabel: gettext('Keep Last'),
|
||||
name: 'keep-last',
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
minValue: 1,
|
||||
allowBlank: true,
|
||||
minValue: 1,
|
||||
allowBlank: true,
|
||||
},
|
||||
{
|
||||
xtype: 'proxmoxintegerfield',
|
||||
fieldLabel: gettext('Keep Daily'),
|
||||
name: 'keep-daily',
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
{
|
||||
xtype: 'proxmoxintegerfield',
|
||||
fieldLabel: gettext('Keep Daily'),
|
||||
name: 'keep-daily',
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
minValue: 1,
|
||||
allowBlank: true,
|
||||
minValue: 1,
|
||||
allowBlank: true,
|
||||
},
|
||||
{
|
||||
xtype: 'proxmoxintegerfield',
|
||||
fieldLabel: gettext('Keep Monthly'),
|
||||
name: 'keep-monthly',
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
{
|
||||
xtype: 'proxmoxintegerfield',
|
||||
fieldLabel: gettext('Keep Monthly'),
|
||||
name: 'keep-monthly',
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
minValue: 1,
|
||||
allowBlank: true,
|
||||
minValue: 1,
|
||||
allowBlank: true,
|
||||
},
|
||||
],
|
||||
column2: [
|
||||
{
|
||||
xtype: 'proxmoxintegerfield',
|
||||
fieldLabel: gettext('Keep Hourly'),
|
||||
name: 'keep-hourly',
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
],
|
||||
|
||||
column2: [
|
||||
{
|
||||
xtype: 'proxmoxintegerfield',
|
||||
fieldLabel: gettext('Keep Hourly'),
|
||||
name: 'keep-hourly',
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
minValue: 1,
|
||||
allowBlank: true,
|
||||
minValue: 1,
|
||||
allowBlank: true,
|
||||
},
|
||||
{
|
||||
xtype: 'proxmoxintegerfield',
|
||||
fieldLabel: gettext('Keep Weekly'),
|
||||
name: 'keep-weekly',
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
{
|
||||
xtype: 'proxmoxintegerfield',
|
||||
fieldLabel: gettext('Keep Weekly'),
|
||||
name: 'keep-weekly',
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
minValue: 1,
|
||||
allowBlank: true,
|
||||
minValue: 1,
|
||||
allowBlank: true,
|
||||
},
|
||||
{
|
||||
xtype: 'proxmoxintegerfield',
|
||||
fieldLabel: gettext('Keep Yearly'),
|
||||
name: 'keep-yearly',
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
{
|
||||
xtype: 'proxmoxintegerfield',
|
||||
fieldLabel: gettext('Keep Yearly'),
|
||||
name: 'keep-yearly',
|
||||
cbind: {
|
||||
deleteEmpty: '{!isCreate}',
|
||||
},
|
||||
minValue: 1,
|
||||
allowBlank: true,
|
||||
},
|
||||
],
|
||||
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
minValue: 1,
|
||||
allowBlank: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
Reference in New Issue
Block a user