mirror of
https://github.com/kenzok8/small-package.git
synced 2026-02-04 13:57:45 +08:00
update 2025-12-21 20:45:42
This commit is contained in:
@@ -7,15 +7,15 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=luci-app-partexp
|
PKG_NAME:=luci-app-partexp
|
||||||
PKG_VERSION:=1.3.2
|
LUCI_TITLE:=LuCI Support for Automatic Partition Mount
|
||||||
PKG_RELEASE:=20250706
|
LUCI_PKGARCH:=all
|
||||||
|
LUCI_DEPENDS:=+fdisk +block-mount +bc +blkid +parted +btrfs-progs +losetup +resize2fs +e2fsprogs +f2fs-tools +kmod-loop
|
||||||
|
PKG_VERSION:=2.0.2
|
||||||
|
PKG_RELEASE:=20251221
|
||||||
|
|
||||||
PKG_LICENSE:=Apache-2.0
|
PKG_LICENSE:=Apache-2.0
|
||||||
PKG_MAINTAINER:=Sirpdboy <herboy2008@gmail.com>
|
PKG_MAINTAINER:=Sirpdboy <herboy2008@gmail.com>
|
||||||
|
|
||||||
LUCI_TITLE:=LuCI Support for Automatic Partition Mount
|
|
||||||
LUCI_DEPENDS:=+fdisk +block-mount +bc +blkid +parted +btrfs-progs +losetup +resize2fs +e2fsprogs +f2fs-tools +kmod-loop
|
|
||||||
LUCI_PKGARCH:=all
|
|
||||||
|
|
||||||
|
|
||||||
include $(TOPDIR)/feeds/luci/luci.mk
|
include $(TOPDIR)/feeds/luci/luci.mk
|
||||||
|
|||||||
@@ -1,127 +0,0 @@
|
|||||||
### 访问数:[] [](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
=
|
|
||||||
# luci-app-partexp
|
|
||||||
|
|
||||||
luci-app-partexp 一键自动格式化分区、扩容、自动挂载插件
|
|
||||||
[](#解决-github-网页上图片显示失败的问题) [](https://t.me/joinchat/AAAAAEpRF88NfOK5vBXGBQ)
|
|
||||||
|
|
||||||
[luci-app-partexp](https://github.com/sirpdboy/luci-app-partexp)
|
|
||||||
======================
|
|
||||||
|
|
||||||
|
|
||||||
请 **认真阅读完毕** 本页面,本页面包含注意事项和如何使用。
|
|
||||||
|
|
||||||
## 功能说明:
|
|
||||||
|
|
||||||
|
|
||||||
#### 一键自动格式化分区、扩容、自动挂载插件,专为OPENWRT设计,简化OPENWRT在分区挂载上烦锁的操作。本插件是sirpdboy耗费大量精力制作测试,请勿删除制作者信息!!
|
|
||||||
|
|
||||||
<!-- TOC -->
|
|
||||||
|
|
||||||
- [partexp](#luci-app-partexp)
|
|
||||||
- [特性](#特性)
|
|
||||||
- [使用方法](#使用方法)
|
|
||||||
- [说明](#说明)
|
|
||||||
- [界面](#界面)
|
|
||||||
- [捐助](#捐助)
|
|
||||||
|
|
||||||
<!-- /TOC -->
|
|
||||||
|
|
||||||
## 版本
|
|
||||||
|
|
||||||
- 最新更新版本号: V1.3.1
|
|
||||||
- 更新日期:2025年3月26日
|
|
||||||
- 更新内容:
|
|
||||||
- 重新整理分区扩容代码,解决一些不合理的地方。
|
|
||||||
- 加入对目标分区的格式,可以指定格式化为ext4,ntfs和Btrfs以及不格式化。
|
|
||||||
- 当做为根目录 /或者 /overlay时,密然会格式化为ext4格式。
|
|
||||||
- 目前在X86的机器上测试完全正常,其它路由设备上未测试。有问题请提交硬盘分区情况和错误提示。
|
|
||||||
|
|
||||||
|
|
||||||
## 特性
|
|
||||||
luci-app-partexp 自动获格式化分区扩容,自动挂载插件
|
|
||||||
|
|
||||||
## 使用方法
|
|
||||||
|
|
||||||
- 将luci-app-partexp添加至 LEDE/OpenWRT 源码的方法。
|
|
||||||
|
|
||||||
### 下载源码方法:
|
|
||||||
|
|
||||||
```Brach
|
|
||||||
|
|
||||||
# 下载源码
|
|
||||||
|
|
||||||
git clone https://github.com/sirpdboy/luci-app-partexp.git package/luci-app-partexp
|
|
||||||
make menuconfig
|
|
||||||
|
|
||||||
```
|
|
||||||
### 配置菜单
|
|
||||||
|
|
||||||
```Brach
|
|
||||||
make menuconfig
|
|
||||||
# 找到 LuCI -> Applications, 选择 luci-app-partexp, 保存后退出。
|
|
||||||
```
|
|
||||||
|
|
||||||
### 编译
|
|
||||||
|
|
||||||
```Brach
|
|
||||||
# 编译固件
|
|
||||||
make package/luci-app-partexp/compile V=s
|
|
||||||
```
|
|
||||||
|
|
||||||
## 说明
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## 界面
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 使用与授权相关说明
|
|
||||||
|
|
||||||
- 本人开源的所有源码,任何引用需注明本处出处,如需修改二次发布必告之本人,未经许可不得做于任何商用用途。
|
|
||||||
|
|
||||||
|
|
||||||
# My other project
|
|
||||||
|
|
||||||
- 网络速度测试 :https://github.com/sirpdboy/NetSpeedTest
|
|
||||||
|
|
||||||
- 定时设置插件 : https://github.com/sirpdboy/luci-app-autotimeset
|
|
||||||
|
|
||||||
- 关机功能插件 : https://github.com/sirpdboy/luci-app-poweroffdevice
|
|
||||||
|
|
||||||
- opentopd主题 : https://github.com/sirpdboy/luci-theme-opentopd
|
|
||||||
|
|
||||||
- kucat 主题: https://github.com/sirpdboy/luci-theme-kucat
|
|
||||||
|
|
||||||
- 家长控制: https://github.com/sirpdboy/luci-theme-parentcontrol
|
|
||||||
|
|
||||||
- 系统高级设置 : https://github.com/sirpdboy/luci-app-advanced
|
|
||||||
|
|
||||||
- ddns-go动态域名: https://github.com/sirpdboy/luci-app-ddns-go
|
|
||||||
|
|
||||||
- 进阶设置(系统高级设置+主题设置kucat/agron/opentopd): https://github.com/sirpdboy/luci-app-advancedplus
|
|
||||||
|
|
||||||
- 设置向导: https://github.com/sirpdboy/luci-app-wizard
|
|
||||||
|
|
||||||
- 分区扩容: https://github.com/sirpdboy/luci-app-partexp
|
|
||||||
|
|
||||||
- lukcy大吉: https://github.com/sirpdboy/luci-app-lukcy
|
|
||||||
|
|
||||||
## 捐助
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
| <img src="https://img.shields.io/badge/-支付宝-F5F5F5.svg" href="#赞助支持本项目-" height="25" alt="图飞了😂"/> | <img src="https://img.shields.io/badge/-微信-F5F5F5.svg" height="25" alt="图飞了😂" href="#赞助支持本项目-"/> |
|
|
||||||
| :-----------------: | :-------------: |
|
|
||||||
| |  |
|
|
||||||
|
|
||||||
<a href="#readme">
|
|
||||||
<img src="https://img.shields.io/badge/-返回顶部-orange.svg" alt="图飞了😂" title="返回顶部" align="right"/>
|
|
||||||
</a>
|
|
||||||
@@ -0,0 +1,963 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022-2025 Sirpdboy <herboy2008@gmail.com>
|
||||||
|
*
|
||||||
|
* Licensed to the public under the Apache License 2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
'require form';
|
||||||
|
'require fs';
|
||||||
|
'require rpc';
|
||||||
|
'require uci';
|
||||||
|
'require ui';
|
||||||
|
'require view';
|
||||||
|
|
||||||
|
// 声明 RPC 接口
|
||||||
|
var callPartExpAutopart = rpc.declare({
|
||||||
|
object: 'partexp',
|
||||||
|
method: 'autopart'
|
||||||
|
});
|
||||||
|
|
||||||
|
var callPartExpGetLog = rpc.declare({
|
||||||
|
object: 'partexp',
|
||||||
|
method: 'get_log',
|
||||||
|
params: ['position']
|
||||||
|
});
|
||||||
|
|
||||||
|
var callPartExpGetDevices = rpc.declare({
|
||||||
|
object: 'partexp',
|
||||||
|
method: 'get_devices'
|
||||||
|
});
|
||||||
|
|
||||||
|
var callPartExpGetStatus = rpc.declare({
|
||||||
|
object: 'partexp',
|
||||||
|
method: 'get_status'
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加保存配置的 RPC 声明
|
||||||
|
var callPartExpSaveConfig = rpc.declare({
|
||||||
|
object: 'partexp',
|
||||||
|
method: 'save_config',
|
||||||
|
params: ['target_function', 'target_disk', 'keep_config', 'format_type']
|
||||||
|
});
|
||||||
|
|
||||||
|
return view.extend({
|
||||||
|
load: function() {
|
||||||
|
return Promise.all([
|
||||||
|
L.resolveDefault(fs.stat('/usr/bin/partexp'), null),
|
||||||
|
L.resolveDefault(fs.stat('/tmp/partexp.log'), null)
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function(data) {
|
||||||
|
var container = E('div', { class: 'cbi-map' });
|
||||||
|
var htmlParts = [
|
||||||
|
'<style>',
|
||||||
|
'.state-ctl .state { display: none !important; }',
|
||||||
|
'.state-ctl.state-ctl-ready .state.state-ready,',
|
||||||
|
'.state-ctl.state-ctl-executing .state.state-executing {',
|
||||||
|
' display: block !important;',
|
||||||
|
'}',
|
||||||
|
'.progress-container {',
|
||||||
|
' width: 100%;',
|
||||||
|
' height: 20px;',
|
||||||
|
' background: rgba(0,0,0,0.2);',
|
||||||
|
' border-radius: 10px;',
|
||||||
|
' display: inline-block;',
|
||||||
|
' margin: 10px 0;',
|
||||||
|
' vertical-align: middle;',
|
||||||
|
' position: relative;',
|
||||||
|
' overflow: hidden;',
|
||||||
|
'}',
|
||||||
|
'.progress-bar {',
|
||||||
|
' height: 100%;',
|
||||||
|
' background: linear-gradient(90deg, #4CAF50, #8BC34A);',
|
||||||
|
' transition: width 0.3s ease-out;',
|
||||||
|
' position: absolute;',
|
||||||
|
' left: 0;',
|
||||||
|
' top: 0;',
|
||||||
|
'}',
|
||||||
|
'.progress-text {',
|
||||||
|
' position: absolute;',
|
||||||
|
' width: 100%;',
|
||||||
|
' text-align: center;',
|
||||||
|
' line-height: 20px;',
|
||||||
|
' font-size: 12px;',
|
||||||
|
' font-weight: bold;',
|
||||||
|
' color: #eee;',
|
||||||
|
' z-index: 1;',
|
||||||
|
' text-shadow: 1px 1px 2px rgba(0,0,0,0.5);',
|
||||||
|
'}',
|
||||||
|
'.error-message {',
|
||||||
|
' color: #dc3545;',
|
||||||
|
'}',
|
||||||
|
'.info-note {',
|
||||||
|
' padding: 10px;',
|
||||||
|
' margin: 10px 0;',
|
||||||
|
' border-radius: 4px;',
|
||||||
|
'}',
|
||||||
|
'.log-view {',
|
||||||
|
' font-family: "Courier New", monospace;',
|
||||||
|
' font-size: 12px;',
|
||||||
|
' height: 300px;',
|
||||||
|
' overflow-y: auto;',
|
||||||
|
' white-space: pre-wrap;',
|
||||||
|
'}',
|
||||||
|
'</style>',
|
||||||
|
'<h2 name="content">' + _('One click partition expansion mounting tool') + '</h2>',
|
||||||
|
'<div class="cbi-section-descr">',
|
||||||
|
' <div class="info-note">',
|
||||||
|
' ' + _('Automatically format and mount the target device partition. If there are multiple partitions, it is recommended to manually delete all partitions before using this tool.') + '<br>',
|
||||||
|
' ' + _('For specific usage, see:') + ' ',
|
||||||
|
' <a href="https://github.com/sirpdboy/luci-app-partexp.git" target="_blank">',
|
||||||
|
' GitHub @partexp',
|
||||||
|
' </a>',
|
||||||
|
' </div>',
|
||||||
|
'</div>',
|
||||||
|
'<div class="state-ctl state-ctl-ready" id="state-container">',
|
||||||
|
' <div class="cbi-section cbi-section-node">',
|
||||||
|
' <div class="state state-ready">',
|
||||||
|
' <form id="partexp-form">',
|
||||||
|
' <div class="cbi-value">',
|
||||||
|
' <label class="cbi-value-title" for="target_function">' + _('Select function') + '</label>',
|
||||||
|
' <div class="cbi-value-field">',
|
||||||
|
' <select class="cbi-input-select" id="target_function" name="target_function">',
|
||||||
|
' <option value="/">' + _('Used to extend to the root directory of EXT4 firmware(Ext4 /)') + '</option>',
|
||||||
|
' <option value="/overlay">' + _('Expand application space overlay (/overlay)') + '</option>',
|
||||||
|
' <option value="/opt">' + _('Used as Docker data disk (/opt)') + '</option>',
|
||||||
|
' <option value="/mnt">' + _('Normal mount and use by device name(/mnt/x1)') + '</option>',
|
||||||
|
' </select>',
|
||||||
|
' <div class="cbi-value-description">' + _('Select the function to be performed') + '</div>',
|
||||||
|
' </div>',
|
||||||
|
' </div>',
|
||||||
|
' <div class="cbi-value">',
|
||||||
|
' <label class="cbi-value-title" for="target_disk">' + _('Destination hard disk') + '</label>',
|
||||||
|
' <div class="cbi-value-field">',
|
||||||
|
' <select class="cbi-input-select" id="target_disk" name="target_disk">',
|
||||||
|
' <option value="">' + _('Loading devices...') + '</option>',
|
||||||
|
' </select>',
|
||||||
|
' <div class="cbi-value-description">' + _('Select the hard disk device to operate') + '</div>',
|
||||||
|
' </div>',
|
||||||
|
' </div>',
|
||||||
|
' <div class="cbi-value">',
|
||||||
|
' <label class="cbi-value-title" for="keep_config">' + _('Keep configuration') + '</label>',
|
||||||
|
' <div class="cbi-value-field">',
|
||||||
|
' <input type="checkbox" class="cbi-input-checkbox" id="keep_config" name="keep_config" value="1" />',
|
||||||
|
' <label for="keep_config">' + _('Tick means to retain the settings') + '</label>',
|
||||||
|
' </div>',
|
||||||
|
' </div>',
|
||||||
|
' <div class="cbi-value">',
|
||||||
|
' <label class="cbi-value-title" for="format_type">' + _('Format system type') + '</label>',
|
||||||
|
' <div class="cbi-value-field">',
|
||||||
|
' <select class="cbi-input-select" id="format_type" name="format_type">',
|
||||||
|
' <option value="0">' + _('No formatting required') + '</option>',
|
||||||
|
' <option value="ext4">' + _('Linux system partition(EXT4)') + '</option>',
|
||||||
|
' <option value="btrfs">' + _('Large capacity storage devices(Btrfs)') + '</option>',
|
||||||
|
' <option value="ntfs">' + _('Windows system partition(NTFS)') + '</option>',
|
||||||
|
' </select>',
|
||||||
|
' </div>',
|
||||||
|
' </div>',
|
||||||
|
' <div class="cbi-value cbi-value-last">',
|
||||||
|
' <label class="cbi-value-title">' + _('Perform operation') + '</label>',
|
||||||
|
' <div class="cbi-value-field">',
|
||||||
|
' <button type="button" class="cbi-button cbi-button-apply" id="execute-btn">',
|
||||||
|
' ' + _('Click to execute') + '',
|
||||||
|
' </button>',
|
||||||
|
' </div>',
|
||||||
|
' </div>',
|
||||||
|
' </form>',
|
||||||
|
' </div>',
|
||||||
|
' <div class="state state-executing">',
|
||||||
|
' <div class="cbi-value">',
|
||||||
|
' <label class="cbi-value-title" id="execute_status">' + _('Starting operation...') + '</label>',
|
||||||
|
' <div class="cbi-value-field">',
|
||||||
|
' <div class="progress-container">',
|
||||||
|
' <div id="progress-bar" class="progress-bar" style="width: 0%"></div>',
|
||||||
|
' <div id="progress-text" class="progress-text">0%</div>',
|
||||||
|
' </div>',
|
||||||
|
' </div>',
|
||||||
|
' </div>',
|
||||||
|
' </div>',
|
||||||
|
' </div>',
|
||||||
|
' <div id="log-section" style="display: block; margin-top: 20px;">',
|
||||||
|
' <div class="cbi-value">',
|
||||||
|
' <label class="cbi-value-title">' + _('Operation Log') + '</label>',
|
||||||
|
' <div class="cbi-value-field">',
|
||||||
|
' <textarea id="log-view" class="log-view" readonly="readonly" rows="15"></textarea>',
|
||||||
|
' </div>',
|
||||||
|
' </div>',
|
||||||
|
' </div>',
|
||||||
|
'</div>'
|
||||||
|
];
|
||||||
|
container.innerHTML = htmlParts.join('');
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// uci 对象已经在全局作用域可用
|
||||||
|
var uci = self.uci || window.uci;
|
||||||
|
setTimeout(function() {
|
||||||
|
self.initDOM();
|
||||||
|
self.bindEvents();
|
||||||
|
self.loadDevices();
|
||||||
|
self.loadSavedConfig();
|
||||||
|
self.checkOperationStatus();
|
||||||
|
self.loadExistingLog();
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return container;
|
||||||
|
},
|
||||||
|
|
||||||
|
initDOM: function() {
|
||||||
|
this.dom = {
|
||||||
|
stateContainer: document.querySelector('#state-container'),
|
||||||
|
targetFunction: document.querySelector('#target_function'),
|
||||||
|
targetDisk: document.querySelector('#target_disk'),
|
||||||
|
keepConfig: document.querySelector('#keep_config'),
|
||||||
|
formatType: document.querySelector('#format_type'),
|
||||||
|
executeBtn: document.querySelector('#execute-btn'),
|
||||||
|
logView: document.querySelector('#log-view'),
|
||||||
|
progressBar: document.querySelector('#progress-bar'),
|
||||||
|
progressText: document.querySelector('#progress-text'),
|
||||||
|
executeStatus: document.querySelector('#execute_status')
|
||||||
|
};
|
||||||
|
|
||||||
|
// 初始化状态变量
|
||||||
|
this.logPosition = '0';
|
||||||
|
this.logPolling = null;
|
||||||
|
this.isRunning = false;
|
||||||
|
this.operationComplete = false;
|
||||||
|
this.pollErrorCount = 0;
|
||||||
|
this.pollingStartTime = 0;
|
||||||
|
this.lastPollTime = 0;
|
||||||
|
this.currentProgress = 0;
|
||||||
|
this.autoSaveTimer = null;
|
||||||
|
this.isNewOperation = false; // 标记是否是新操作
|
||||||
|
},
|
||||||
|
|
||||||
|
bindEvents: function() {
|
||||||
|
var self = this;
|
||||||
|
if (this.dom.executeBtn) {
|
||||||
|
this.dom.executeBtn.addEventListener('click', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
self.executeOperation();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 表单变化事件 - 自动保存
|
||||||
|
[this.dom.targetFunction, this.dom.targetDisk, this.dom.formatType].forEach(function(element) {
|
||||||
|
if (element) {
|
||||||
|
element.addEventListener('change', function() {
|
||||||
|
self.autoSaveConfig();
|
||||||
|
self.updateFormVisibility();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 复选框特殊处理
|
||||||
|
if (this.dom.keepConfig) {
|
||||||
|
this.dom.keepConfig.addEventListener('click', function() {
|
||||||
|
self.autoSaveConfig();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化表单可见性
|
||||||
|
if (this.dom.targetFunction) {
|
||||||
|
this.updateFormVisibility();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 加载设备列表
|
||||||
|
loadDevices: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
callPartExpGetDevices().then(function(response) {
|
||||||
|
if (!response || !response.devices || response.devices.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空设备列表
|
||||||
|
if (self.dom.targetDisk) {
|
||||||
|
self.dom.targetDisk.innerHTML = '';
|
||||||
|
|
||||||
|
// 添加设备选项
|
||||||
|
response.devices.forEach(function(device) {
|
||||||
|
var option = document.createElement('option');
|
||||||
|
option.value = device.name;
|
||||||
|
option.textContent = device.name + ' (' + device.dev + ', ' + device.size + ' MB)';
|
||||||
|
self.dom.targetDisk.appendChild(option);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}).catch(function(error) {
|
||||||
|
console.error('Failed to load devices:', error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 加载现有的日志文件内容
|
||||||
|
loadExistingLog: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// 初始化时获取现有日志内容
|
||||||
|
callPartExpGetLog('0').then(function(response) {
|
||||||
|
if (response && response.log) {
|
||||||
|
var logContent = response.log.toString().trim();
|
||||||
|
if (logContent && self.dom.logView) {
|
||||||
|
// 显示现有日志内容
|
||||||
|
self.dom.logView.value = logContent;
|
||||||
|
|
||||||
|
// 自动滚动到底部
|
||||||
|
setTimeout(function() {
|
||||||
|
if (self.dom.logView && self.dom.logView.value) {
|
||||||
|
self.dom.logView.scrollTop = self.dom.logView.scrollHeight;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
// 更新日志位置
|
||||||
|
if (response.position) {
|
||||||
|
self.logPosition = response.position;
|
||||||
|
}
|
||||||
|
if (!self.isRunning && logContent.includes('正在执行') && !logContent.includes('操作完成')) {
|
||||||
|
self.isRunning = true;
|
||||||
|
self.switchState('executing');
|
||||||
|
self.startLogPolling();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(function(error) {
|
||||||
|
console.error('Failed to load existing log:', error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
loadSavedConfig: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
return fs.read('/etc/config/partexp').then(function(content) {
|
||||||
|
if (!content) {
|
||||||
|
self.setDefaultConfig();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析配置文件
|
||||||
|
var lines = content.split('\n');
|
||||||
|
var config = {};
|
||||||
|
|
||||||
|
lines.forEach(function(line) {
|
||||||
|
line = line.trim();
|
||||||
|
if (line.startsWith('option')) {
|
||||||
|
var parts = line.split(/\s+/);
|
||||||
|
if (parts.length >= 3) {
|
||||||
|
var key = parts[1];
|
||||||
|
var value = parts.slice(2).join(' ').replace(/^['"]|['"]$/g, '');
|
||||||
|
config[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设置表单值
|
||||||
|
if (self.dom.targetFunction) {
|
||||||
|
self.dom.targetFunction.value = config.target_function || '/opt';
|
||||||
|
}
|
||||||
|
if (self.dom.targetDisk && config.target_disk) {
|
||||||
|
// 等待设备加载完成后设置
|
||||||
|
setTimeout(function() {
|
||||||
|
if (self.dom.targetDisk) {
|
||||||
|
self.dom.targetDisk.value = config.target_disk;
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
if (self.dom.keepConfig) {
|
||||||
|
self.dom.keepConfig.checked = (config.keep_config === '1');
|
||||||
|
}
|
||||||
|
if (self.dom.formatType) {
|
||||||
|
self.dom.formatType.value = config.format_type || '0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新配置缓存
|
||||||
|
self.configCache = config;
|
||||||
|
|
||||||
|
// 更新表单可见性
|
||||||
|
self.updateFormVisibility();
|
||||||
|
|
||||||
|
}).catch(function(error) {
|
||||||
|
console.log('Failed to load config:', error);
|
||||||
|
self.setDefaultConfig();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 设置默认配置
|
||||||
|
setDefaultConfig: function() {
|
||||||
|
if (this.dom.targetFunction) {
|
||||||
|
this.dom.targetFunction.value = '/opt';
|
||||||
|
}
|
||||||
|
if (this.dom.formatType) {
|
||||||
|
this.dom.formatType.value = '0';
|
||||||
|
}
|
||||||
|
if (this.dom.keepConfig) {
|
||||||
|
this.dom.keepConfig.checked = false;
|
||||||
|
}
|
||||||
|
this.updateFormVisibility();
|
||||||
|
|
||||||
|
this.configCache = {
|
||||||
|
target_function: '/opt',
|
||||||
|
target_disk: '',
|
||||||
|
keep_config: '0',
|
||||||
|
format_type: '0'
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
// 自动保存配置(防抖处理)
|
||||||
|
autoSaveConfig: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// 清除之前的定时器
|
||||||
|
if (this.autoSaveTimer) {
|
||||||
|
clearTimeout(this.autoSaveTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置新的定时器,1.5秒后保存
|
||||||
|
this.autoSaveTimer = setTimeout(function() {
|
||||||
|
self.saveCurrentConfig();
|
||||||
|
}, 1500);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 保存当前配置
|
||||||
|
saveCurrentConfig: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// 获取当前表单值
|
||||||
|
var targetFunction = this.dom.targetFunction ? this.dom.targetFunction.value : '/opt';
|
||||||
|
var targetDisk = this.dom.targetDisk ? this.dom.targetDisk.value : '';
|
||||||
|
var keepConfig = this.dom.keepConfig ? this.dom.keepConfig.checked : false;
|
||||||
|
var formatType = this.dom.formatType ? this.dom.formatType.value : '0';
|
||||||
|
if (callPartExpSaveConfig) {
|
||||||
|
return callPartExpSaveConfig(
|
||||||
|
targetFunction,
|
||||||
|
targetDisk,
|
||||||
|
keepConfig ? '1' : '0',
|
||||||
|
formatType
|
||||||
|
).then(function(response) {
|
||||||
|
if (response && response.success) {
|
||||||
|
|
||||||
|
self.configCache = {
|
||||||
|
target_function: targetFunction,
|
||||||
|
target_disk: targetDisk,
|
||||||
|
keep_config: keepConfig ? '1' : '0',
|
||||||
|
format_type: formatType
|
||||||
|
};
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
console.warn('RPC save failed, falling back to file write');
|
||||||
|
return self.saveConfigToFile(targetFunction, targetDisk, keepConfig, formatType);
|
||||||
|
}
|
||||||
|
}).catch(function(error) {
|
||||||
|
console.error('RPC save config error:', error);
|
||||||
|
return self.saveConfigToFile(targetFunction, targetDisk, keepConfig, formatType);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 如果 RPC 不可用,直接使用文件写入
|
||||||
|
return self.saveConfigToFile(targetFunction, targetDisk, keepConfig, formatType);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 备选方案:直接写入配置文件
|
||||||
|
saveConfigToFile: function(targetFunction, targetDisk, keepConfig, formatType) {
|
||||||
|
var configContent = [
|
||||||
|
'# Auto-generated by partexp',
|
||||||
|
'',
|
||||||
|
'config global global',
|
||||||
|
"\toption target_function '" + targetFunction + "'",
|
||||||
|
"\toption target_disk '" + targetDisk + "'",
|
||||||
|
"\toption keep_config '" + (keepConfig ? '1' : '0') + "'",
|
||||||
|
"\toption format_type '" + formatType + "'",
|
||||||
|
''
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
return fs.write('/etc/config/partexp', configContent).then(function() {
|
||||||
|
console.log('Settings saved to file /etc/config/partexp');
|
||||||
|
return true;
|
||||||
|
}).catch(function(error) {
|
||||||
|
console.error('Failed to save settings to file:', error);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 执行操作
|
||||||
|
executeOperation: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// 先保存配置
|
||||||
|
this.saveCurrentConfig();
|
||||||
|
var target_function = this.dom.targetFunction.value;
|
||||||
|
var target_disk = this.dom.targetDisk.value;
|
||||||
|
|
||||||
|
if (target_function !== '/' && (!target_disk || target_disk.trim() === '')) {
|
||||||
|
alert(_('Please select a target disk'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认操作
|
||||||
|
var confirmMessage = _('Are you sure you want to execute partition expansion?') + '\n\n' +
|
||||||
|
_('Function:') + ' ' + this.getFunctionDescription(target_function) + '\n' +
|
||||||
|
(target_function !== '/' ? _('Disk:') + ' ' + target_disk + '\n' : '') +
|
||||||
|
(target_function === '/' || target_function === '/overlay' ?
|
||||||
|
_('Keep config:') + ' ' + (this.dom.keepConfig.checked ? _('Yes') : _('No')) + '\n' : '') +
|
||||||
|
(target_function === '/opt' || target_function === '/dev' ?
|
||||||
|
_('Format type:') + ' ' + this.getFormatTypeDescription(this.dom.formatType.value) + '\n' : '') +
|
||||||
|
'\n' + _('This operation may take several minutes.');
|
||||||
|
|
||||||
|
if (!confirm(confirmMessage)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置操作状态
|
||||||
|
this.resetOperationState();
|
||||||
|
|
||||||
|
// 标记为新操作开始
|
||||||
|
this.isNewOperation = true;
|
||||||
|
|
||||||
|
if (this.dom.logView) {
|
||||||
|
this.dom.logView.value = _('正在启动操作...');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新按钮状态
|
||||||
|
if (this.dom.executeBtn) {
|
||||||
|
this.dom.executeBtn.disabled = true;
|
||||||
|
this.dom.executeBtn.textContent = _('Executing...');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换到执行状态
|
||||||
|
this.switchState('executing');
|
||||||
|
|
||||||
|
// 开始进度显示
|
||||||
|
this.updateProgress(5, _('Starting operation...'));
|
||||||
|
|
||||||
|
// 调用分区操作
|
||||||
|
callPartExpAutopart()
|
||||||
|
.then(function(response) {
|
||||||
|
if (response && response.success) {
|
||||||
|
// 操作开始成功
|
||||||
|
self.isRunning = true;
|
||||||
|
self.operationComplete = false;
|
||||||
|
self.startLogPolling();
|
||||||
|
|
||||||
|
if (self.dom.executeStatus) {
|
||||||
|
self.dom.executeStatus.textContent = _('Operation started successfully');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 操作启动失败
|
||||||
|
var errorMsg = response && response.message ? response.message : _('Operation failed');
|
||||||
|
self.handleOperationError(errorMsg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(function(error) {
|
||||||
|
console.error('Operation failed:', error);
|
||||||
|
self.handleOperationError(_('Failed to start operation:') + ' ' + (error.message || _('Unknown error')));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 重置操作状态
|
||||||
|
resetOperationState: function() {
|
||||||
|
this.logPosition = '0';
|
||||||
|
this.isRunning = true;
|
||||||
|
this.operationComplete = false;
|
||||||
|
this.pollErrorCount = 0;
|
||||||
|
this.pollingStartTime = Date.now();
|
||||||
|
this.lastPollTime = 0;
|
||||||
|
this.currentProgress = 0;
|
||||||
|
|
||||||
|
// 重置进度条
|
||||||
|
this.updateProgress(0, _('Starting operation...'));
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理操作错误
|
||||||
|
handleOperationError: function(errorMsg) {
|
||||||
|
alert(errorMsg);
|
||||||
|
if (this.dom.executeBtn) {
|
||||||
|
this.dom.executeBtn.disabled = false;
|
||||||
|
this.dom.executeBtn.textContent = _('Click to execute');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.switchState('ready');
|
||||||
|
this.stopLogPolling();
|
||||||
|
|
||||||
|
// 在日志中显示错误信息
|
||||||
|
if (this.dom.logView) {
|
||||||
|
var currentLog = this.dom.logView.value || '';
|
||||||
|
this.dom.logView.value = currentLog + '\n\n' + _('操作失败:') + ' ' + errorMsg;
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.dom.logView) {
|
||||||
|
this.dom.logView.scrollTop = this.dom.logView.scrollHeight;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新表单可见性
|
||||||
|
updateFormVisibility: function() {
|
||||||
|
if (!this.dom.targetFunction || !this.dom.targetDisk ||
|
||||||
|
!this.dom.keepConfig || !this.dom.formatType) return;
|
||||||
|
|
||||||
|
var func = this.dom.targetFunction.value;
|
||||||
|
var diskDiv = this.dom.targetDisk.closest('.cbi-value');
|
||||||
|
var keepDiv = this.dom.keepConfig.closest('.cbi-value');
|
||||||
|
var formatDiv = this.dom.formatType.closest('.cbi-value');
|
||||||
|
|
||||||
|
if (!diskDiv || !keepDiv || !formatDiv) return;
|
||||||
|
|
||||||
|
if (func === '/') {
|
||||||
|
diskDiv.style.display = 'none';
|
||||||
|
formatDiv.style.display = 'none';
|
||||||
|
keepDiv.style.display = 'block';
|
||||||
|
} else if (func === '/overlay') {
|
||||||
|
diskDiv.style.display = 'block';
|
||||||
|
formatDiv.style.display = 'none';
|
||||||
|
keepDiv.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
diskDiv.style.display = 'block';
|
||||||
|
formatDiv.style.display = 'block';
|
||||||
|
keepDiv.style.display = 'none';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 检查操作状态
|
||||||
|
checkOperationStatus: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
callPartExpGetStatus().then(function(response) {
|
||||||
|
if (response && response.running) {
|
||||||
|
// 有操作在进行中
|
||||||
|
self.isRunning = true;
|
||||||
|
self.switchState('executing');
|
||||||
|
self.startLogPolling();
|
||||||
|
|
||||||
|
// 禁用执行按钮
|
||||||
|
if (self.dom.executeBtn) {
|
||||||
|
self.dom.executeBtn.disabled = true;
|
||||||
|
self.dom.executeBtn.textContent = _('Operation in progress...');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新状态
|
||||||
|
if (self.dom.executeStatus) {
|
||||||
|
self.dom.executeStatus.textContent = _('Operation in progress...');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(function(error) {
|
||||||
|
console.error('Failed to check operation status:', error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 开始轮询日志
|
||||||
|
startLogPolling: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// 停止现有的轮询
|
||||||
|
this.stopLogPolling();
|
||||||
|
|
||||||
|
// 重置状态
|
||||||
|
this.pollErrorCount = 0;
|
||||||
|
this.pollingStartTime = Date.now();
|
||||||
|
this.lastPollTime = 0;
|
||||||
|
|
||||||
|
// 更新进度显示
|
||||||
|
this.updateProgress(10, _('Operation in progress...'));
|
||||||
|
|
||||||
|
// 开始轮询
|
||||||
|
this.logPolling = setInterval(function() {
|
||||||
|
// 检查是否超时(20分钟超时)
|
||||||
|
if (Date.now() - self.pollingStartTime > 20 * 60 * 1000) {
|
||||||
|
console.error('Operation timeout');
|
||||||
|
self.stopLogPolling();
|
||||||
|
self.isRunning = false;
|
||||||
|
|
||||||
|
// 显示超时信息
|
||||||
|
if (self.dom.logView) {
|
||||||
|
var currentLog = self.dom.logView.value || '';
|
||||||
|
self.dom.logView.value = currentLog + '\n\n[超时] 操作超过20分钟未完成,请检查系统';
|
||||||
|
setTimeout(() => {
|
||||||
|
if (self.dom.logView) {
|
||||||
|
self.dom.logView.scrollTop = self.dom.logView.scrollHeight;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.switchState('ready');
|
||||||
|
|
||||||
|
if (self.dom.executeBtn) {
|
||||||
|
self.dom.executeBtn.disabled = false;
|
||||||
|
self.dom.executeBtn.textContent = _('Click to execute');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pollLog();
|
||||||
|
}, 3000); // 每3秒轮询一次,减少频率
|
||||||
|
},
|
||||||
|
|
||||||
|
pollLog: function() {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (!this.isRunning) {
|
||||||
|
this.stopLogPolling();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pollStartTime = Date.now();
|
||||||
|
|
||||||
|
// 总是从位置0开始获取完整日志内容
|
||||||
|
callPartExpGetLog('0').then(function(response) {
|
||||||
|
if (!response) {
|
||||||
|
console.error('No response from log polling');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pollStartTime < self.lastPollTime) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.lastPollTime = pollStartTime;
|
||||||
|
|
||||||
|
// 处理日志内容
|
||||||
|
if (response.log !== undefined) {
|
||||||
|
var logContent = response.log.toString().trim();
|
||||||
|
|
||||||
|
if (response.position) {
|
||||||
|
self.logPosition = response.position;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.dom.logView) {
|
||||||
|
if (logContent !== '') {
|
||||||
|
self.dom.logView.value = logContent;
|
||||||
|
|
||||||
|
// 自动滚动到底部
|
||||||
|
setTimeout(function() {
|
||||||
|
if (self.dom.logView && self.dom.logView.value) {
|
||||||
|
self.dom.logView.scrollTop = self.dom.logView.scrollHeight;
|
||||||
|
}
|
||||||
|
}, 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新进度
|
||||||
|
self.parseAndUpdateProgress(logContent);
|
||||||
|
|
||||||
|
// 检查操作是否完成
|
||||||
|
if (self.checkOperationComplete(logContent)) {
|
||||||
|
self.handleOperationComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查RPC返回的完成状态
|
||||||
|
if (response.complete) {
|
||||||
|
self.handleOperationComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置错误计数
|
||||||
|
self.pollErrorCount = 0;
|
||||||
|
|
||||||
|
}).catch(function(error) {
|
||||||
|
console.error('Log polling error:', error);
|
||||||
|
|
||||||
|
// 如果多次失败,停止轮询
|
||||||
|
self.pollErrorCount = (self.pollErrorCount || 0) + 1;
|
||||||
|
if (self.pollErrorCount > 5) {
|
||||||
|
console.error('Too many polling errors, stopping');
|
||||||
|
self.stopLogPolling();
|
||||||
|
self.isRunning = false;
|
||||||
|
self.switchState('ready');
|
||||||
|
|
||||||
|
if (self.dom.executeBtn) {
|
||||||
|
self.dom.executeBtn.disabled = false;
|
||||||
|
self.dom.executeBtn.textContent = _('Click to execute');
|
||||||
|
}
|
||||||
|
// 显示错误信息
|
||||||
|
if (self.dom.logView) {
|
||||||
|
var currentLog = self.dom.logView.value || '';
|
||||||
|
self.dom.logView.value = currentLog + '\n\n[错误] 日志轮询失败,请刷新页面查看最新状态';
|
||||||
|
setTimeout(() => {
|
||||||
|
if (self.dom.logView) {
|
||||||
|
self.dom.logView.scrollTop = self.dom.logView.scrollHeight;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 检查操作是否完成
|
||||||
|
checkOperationComplete: function(logText) {
|
||||||
|
if (!logText) return false;
|
||||||
|
|
||||||
|
// 检查日志中是否包含操作完成标记
|
||||||
|
var completeMarkers = [
|
||||||
|
'重启设备',
|
||||||
|
'操作完成'
|
||||||
|
];
|
||||||
|
|
||||||
|
for (var i = 0; i < completeMarkers.length; i++) {
|
||||||
|
if (logText.includes(completeMarkers[i])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理操作完成
|
||||||
|
handleOperationComplete: function() {
|
||||||
|
if (this.operationComplete) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.operationComplete = true;
|
||||||
|
this.isRunning = false;
|
||||||
|
this.isNewOperation = false;
|
||||||
|
|
||||||
|
// 立即停止轮询
|
||||||
|
this.stopLogPolling();
|
||||||
|
if (this.dom.logView) {
|
||||||
|
var currentLog = this.dom.logView.value || '';
|
||||||
|
if (!currentLog.includes('操作完成')) {
|
||||||
|
this.dom.logView.value = currentLog;
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.dom.logView) {
|
||||||
|
this.dom.logView.scrollTop = this.dom.logView.scrollHeight;
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 进度条显示100%
|
||||||
|
this.updateProgress(100, _('Operation completed'));
|
||||||
|
|
||||||
|
// 启用执行按钮
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.dom.executeBtn) {
|
||||||
|
this.dom.executeBtn.disabled = false;
|
||||||
|
this.dom.executeBtn.textContent = _('Click to execute');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换回就绪状态
|
||||||
|
setTimeout(() => {
|
||||||
|
this.switchState('ready');
|
||||||
|
}, 3000);
|
||||||
|
}, 2000);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 解析并更新进度
|
||||||
|
parseAndUpdateProgress: function(logText) {
|
||||||
|
if (!logText || !this.dom.executeStatus) return;
|
||||||
|
|
||||||
|
// 尝试从日志中提取进度信息
|
||||||
|
var percent = 0;
|
||||||
|
var statusMessage = _('Operation in progress...');
|
||||||
|
|
||||||
|
if (logText.includes('100%') || logText.includes('操作完成') || logText.includes('扩容成功')) {
|
||||||
|
percent = 100;
|
||||||
|
statusMessage = _('Operation completed');
|
||||||
|
} else if ( logText.includes('错误') || logText.includes('error')) {
|
||||||
|
// 错误情况,不更新进度
|
||||||
|
return;
|
||||||
|
} else if (logText.includes('分区扩容和挂载到') || logText.includes('正在挂载')) {
|
||||||
|
percent = 90;
|
||||||
|
statusMessage = _('Getting device information');
|
||||||
|
} else if (logText.includes('检测设备')) {
|
||||||
|
percent = 60;
|
||||||
|
statusMessage = _('Checking partition format');
|
||||||
|
} else if (logText.includes('开始检测目标')) {
|
||||||
|
percent = 50;
|
||||||
|
statusMessage = _('Checking target device');
|
||||||
|
} else if (logText.includes('定位到操作目标设备分区')) {
|
||||||
|
percent = 40;
|
||||||
|
statusMessage = _('Locating target partition');
|
||||||
|
} else if (logText.includes('目标盘') && logText.includes('有剩余空间')) {
|
||||||
|
percent = 30;
|
||||||
|
statusMessage = _('Checking free space');
|
||||||
|
} else if (logText.includes('操作功能')) {
|
||||||
|
percent = 20;
|
||||||
|
statusMessage = _('Starting operation');
|
||||||
|
} else if (logText.includes('开始执行') || logText.includes('Starting')) {
|
||||||
|
percent = 10;
|
||||||
|
statusMessage = _('Initializing...');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保进度不会倒退
|
||||||
|
if (percent > 0) {
|
||||||
|
this.currentProgress = Math.max(this.currentProgress || 0, percent);
|
||||||
|
} else {
|
||||||
|
// 如果没有明确的进度标记,逐渐增加进度
|
||||||
|
this.currentProgress = Math.min(90, (this.currentProgress || 0) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新进度显示
|
||||||
|
this.updateProgress(this.currentProgress, statusMessage);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新进度显示
|
||||||
|
updateProgress: function(percent, message) {
|
||||||
|
if (!this.dom.progressBar || !this.dom.progressText || !this.dom.executeStatus) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保百分比在有效范围内
|
||||||
|
percent = Math.max(0, Math.min(100, percent));
|
||||||
|
|
||||||
|
// 更新进度条
|
||||||
|
this.dom.progressBar.style.width = percent + '%';
|
||||||
|
|
||||||
|
// 更新进度文本
|
||||||
|
this.dom.progressText.textContent = percent + '%';
|
||||||
|
|
||||||
|
// 更新状态消息
|
||||||
|
this.dom.executeStatus.textContent = message;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 停止轮询日志
|
||||||
|
stopLogPolling: function() {
|
||||||
|
if (this.logPolling) {
|
||||||
|
clearInterval(this.logPolling);
|
||||||
|
this.logPolling = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 切换状态
|
||||||
|
switchState: function(to) {
|
||||||
|
if (!this.dom.stateContainer) return;
|
||||||
|
|
||||||
|
// 移除所有状态类
|
||||||
|
this.dom.stateContainer.classList.remove(
|
||||||
|
'state-ctl-ready',
|
||||||
|
'state-ctl-executing'
|
||||||
|
);
|
||||||
|
|
||||||
|
// 添加新状态类
|
||||||
|
this.dom.stateContainer.classList.add('state-ctl-' + to);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取功能描述
|
||||||
|
getFunctionDescription: function(func) {
|
||||||
|
switch(func) {
|
||||||
|
case '/': return _('Extend to root directory');
|
||||||
|
case '/overlay': return _('Expand overlay');
|
||||||
|
case '/opt': return _('Docker data disk');
|
||||||
|
case '/dev': return _('Normal mount');
|
||||||
|
default: return func;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取格式化类型描述
|
||||||
|
getFormatTypeDescription: function(type) {
|
||||||
|
switch(type) {
|
||||||
|
case '0': return _('No formatting');
|
||||||
|
case 'ext4': return _('EXT4');
|
||||||
|
case 'btrfs': return _('Btrfs');
|
||||||
|
case 'ntfs': return _('NTFS');
|
||||||
|
default: return type;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 页面生命周期方法
|
||||||
|
handleSaveApply: null,
|
||||||
|
handleSave: null,
|
||||||
|
handleReset: null
|
||||||
|
});
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
--[[
|
|
||||||
LuCI - Lua Configuration Partition Expansion
|
|
||||||
Copyright (C) 2022-2025 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/partexp
|
|
||||||
]]--
|
|
||||||
|
|
||||||
local fs = require "nixio.fs"
|
|
||||||
local http = require "luci.http"
|
|
||||||
local uci = require"luci.model.uci".cursor()
|
|
||||||
local name = 'partexp'
|
|
||||||
|
|
||||||
module("luci.controller.partexp", package.seeall)
|
|
||||||
|
|
||||||
function index()
|
|
||||||
local e = entry({"admin","system","partexp"},alias("admin", "system", "partexp", "global"),_("Partition Expansion"), 54)
|
|
||||||
e.dependent = false
|
|
||||||
e.acl_depends = { "luci-app-partexp" }
|
|
||||||
entry({"admin","system","partexp","global"}, cbi('partexp/global', {hideapplybtn = true, hidesavebtn = true, hideresetbtn = true}), _('Partition Expansion'), 10).leaf = true
|
|
||||||
entry({"admin", "system", "partexp","partexprun"}, call("partexprun"))
|
|
||||||
entry({"admin", "system", "partexp", "check"}, call("act_check"))
|
|
||||||
end
|
|
||||||
function act_check()
|
|
||||||
|
|
||||||
http.prepare_content("text/plain; charset=utf-8")
|
|
||||||
local f=io.open("/tmp/partexp.log", "r+")
|
|
||||||
local fdp=fs.readfile("/tmp/lucilogpos") or 0
|
|
||||||
f:seek("set",fdp)
|
|
||||||
local a=f:read(2048000) or ""
|
|
||||||
fdp=f:seek()
|
|
||||||
fs.writefile("/tmp/lucilogpos",tostring(fdp))
|
|
||||||
f:close()
|
|
||||||
http.write(a)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function partexprun()
|
|
||||||
local kconfig = http.formvalue('kconfig')
|
|
||||||
local eformat = http.formvalue('eformat')
|
|
||||||
local targetf = http.formvalue('targetf')
|
|
||||||
local targetd = http.formvalue('targetd')
|
|
||||||
uci:set(name, 'global', 'target_disk', targetd)
|
|
||||||
uci:set(name, 'global', 'target_function', targetf)
|
|
||||||
uci:set(name, 'global', 'format_type', eformat)
|
|
||||||
uci:set(name, 'global', 'keep_config', kconfig)
|
|
||||||
uci:commit(name)
|
|
||||||
fs.writefile("/tmp/lucilogpos","0")
|
|
||||||
http.prepare_content("application/json")
|
|
||||||
http.write('')
|
|
||||||
luci.sys.exec("/etc/init.d/partexp autopart > /tmp/partexp.log 2>&1 &")
|
|
||||||
end
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
--[[
|
|
||||||
LuCI - Lua Configuration Interface
|
|
||||||
Copyright (C) 2022-2025 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/luci-app-partexp
|
|
||||||
]]--
|
|
||||||
local fs = require "nixio.fs"
|
|
||||||
local util = require "nixio.util"
|
|
||||||
local tp = require "luci.template.parser"
|
|
||||||
local uci=luci.model.uci.cursor()
|
|
||||||
luci.sys.exec("echo '-' >/tmp/partexp.log&&echo 1 > /tmp/lucilogpos" )
|
|
||||||
local target_devnames = {}
|
|
||||||
for dev in fs.dir("/dev") do
|
|
||||||
if dev:match("^sd[a-z]$")
|
|
||||||
or dev:match("^mmcblk%d+$")
|
|
||||||
or dev:match("^sata[a-z]$")
|
|
||||||
or dev:match("^nvme%d+n%d+$")
|
|
||||||
or dev:match("^vd[a-z]")
|
|
||||||
then
|
|
||||||
table.insert(target_devnames, dev)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local devices = {}
|
|
||||||
for i, bname in pairs(target_devnames) do
|
|
||||||
local device_info = {}
|
|
||||||
local device = "/dev/" .. bname
|
|
||||||
device_info["name"] = bname
|
|
||||||
device_info["dev"] = device
|
|
||||||
|
|
||||||
s = tonumber((fs.readfile("/sys/class/block/%s/size" % bname)))
|
|
||||||
device_info["size"] = s and math.floor(s / 2048)
|
|
||||||
|
|
||||||
devices[#devices+1] = device_info
|
|
||||||
end
|
|
||||||
|
|
||||||
local m,t,e
|
|
||||||
m = Map("partexp", "<font color='green'>" .. translate("One click partition expansion mounting tool") .."</font>",
|
|
||||||
translate( "Automatically format and mount the target device partition. If there are multiple partitions, it is recommended to manually delete all partitions before using this tool.<br/>For specific usage, see:") ..translate("<a href=\'https://github.com/sirpdboy/luci-app-partexp.git' target=\'_blank\'>GitHub @sirpdboy:luci-app-partexp</a>") )
|
|
||||||
|
|
||||||
t=m:section(TypedSection,"global")
|
|
||||||
t.anonymous=true
|
|
||||||
|
|
||||||
e=t:option(ListValue,"target_function", translate("Select function"),translate("Select the function to be performed"))
|
|
||||||
e:value("/", translate("Used to extend to the root directory of EXT4 firmware(Ext4 /)"))
|
|
||||||
e:value("/overlay", translate("Expand application space overlay (/overlay)"))
|
|
||||||
e:value("/opt", translate("Used as Docker data disk (/opt)"))
|
|
||||||
e:value("/dev", translate("Normal mount and use by device name(/mnt/x1)"))
|
|
||||||
e.default="/opt"
|
|
||||||
|
|
||||||
e=t:option(ListValue,"target_disk", translate("Destination hard disk"),translate("Select the hard disk device to operate"))
|
|
||||||
e:depends("target_function", "/overlay")
|
|
||||||
e:depends("target_function", "/opt")
|
|
||||||
e:depends("target_function", "/dev")
|
|
||||||
for i, d in ipairs(devices) do
|
|
||||||
if d.name and d.size then
|
|
||||||
e:value(d.name, "%s (%s, %d MB)" %{ d.name, d.dev, d.size })
|
|
||||||
elseif d.name then
|
|
||||||
e:value(d.name, "%s (%s)" %{ d.name, d.dev })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
e=t:option(Flag,"keep_config",translate("Keep configuration"),translate("Tick means to retain the settings"))
|
|
||||||
e:depends("target_function", "/overlay")
|
|
||||||
e:depends("target_function", "/")
|
|
||||||
e.default=0
|
|
||||||
|
|
||||||
e=t:option(ListValue,'format_type', translate('Format system type'))
|
|
||||||
e:depends("target_function", "/opt")
|
|
||||||
e:depends("target_function", "/dev")
|
|
||||||
e:value("0", translate("No formatting required"))
|
|
||||||
e:value("ext4", translate("Linux system partition(EXT4)"))
|
|
||||||
e:value("btrfs", translate("Large capacity storage devices(Btrfs)"))
|
|
||||||
e:value("ntfs", translate("Windows system partition(NTFS)"))
|
|
||||||
e.default="0"
|
|
||||||
|
|
||||||
e=t:option(Button, "restart", translate("Perform operation"))
|
|
||||||
e.inputtitle=translate("Click to execute")
|
|
||||||
e.rawhtml=true
|
|
||||||
e.template ='partexp'
|
|
||||||
|
|
||||||
return m
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
<%#
|
|
||||||
Copyright (C) 2022-2024 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/partexp
|
|
||||||
-%>
|
|
||||||
<%+cbi/valueheader%>
|
|
||||||
<%local fs=require"nixio.fs"%>
|
|
||||||
<input type="button" class="btn cbi-button cbi-button-apply" id="apply_run_button" value="<%:Click to execute%>" onclick=" return apply_run(this) "/>
|
|
||||||
<div id="logview" style="display:none">
|
|
||||||
<input type="checkbox" id="reversetag" value="reverse" onclick=" return reverselog()" style="vertical-align:middle;height: auto;"><%:reverse%></input>
|
|
||||||
<textarea id="cbid.logview.1.conf" class="cbi-input-textarea" style="width: 100%;display:block;" data-update="change" rows="20" cols="80" readonly="readonly" > </textarea>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript">//<![CDATA[
|
|
||||||
|
|
||||||
const PARTEXP_RUN_URL = '<%=luci.dispatcher.build_url("admin", "system", "partexp","partexprun")%>';
|
|
||||||
const PARTEXP_CHECK = '<%=luci.dispatcher.build_url("admin", "system", "partexp","check")%>';
|
|
||||||
|
|
||||||
var checkbtn = document.getElementById('apply_run_button');
|
|
||||||
|
|
||||||
var islogreverse = false;
|
|
||||||
function reverselog(){
|
|
||||||
var lv = document.getElementById('cbid.logview.1.conf');
|
|
||||||
lv.innerHTML=lv.innerHTML.split('\n').reverse().join('\n')
|
|
||||||
if (islogreverse){
|
|
||||||
islogreverse=false;
|
|
||||||
}else{
|
|
||||||
islogreverse=true;
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
function apply_run(btn){
|
|
||||||
|
|
||||||
var sid='global'
|
|
||||||
var opt={
|
|
||||||
base:"cbid.partexp."+sid,
|
|
||||||
get:function(opt){
|
|
||||||
var id=this.base+'.'+opt;
|
|
||||||
var obj=document.getElementsByName(id)[0] || document.getElementsByClassName(id)[0] || document.getElementById(id)
|
|
||||||
if (obj){
|
|
||||||
return obj;
|
|
||||||
}else{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getlist:function(opt){
|
|
||||||
var id=this.base+'.'+opt;
|
|
||||||
var objs=document.getElementsByName(id) || document.getElementsByClassName(id);
|
|
||||||
var ret=[];
|
|
||||||
if (objs){
|
|
||||||
for (var i=0;i < objs.length;i++){
|
|
||||||
ret[i]=objs[i].value;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
alert("<%:Fatal on get option,please help in debug%>:"+opt);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
},
|
|
||||||
query:function(param,src,tval="1",fval="0"){
|
|
||||||
var ret="&"+param+"=";
|
|
||||||
var obj=this.get(src);
|
|
||||||
if (obj){
|
|
||||||
if (obj.type=="checkbox"){
|
|
||||||
return ret+(obj.checked==true ? tval:fval);
|
|
||||||
}else{
|
|
||||||
return ret+encodeURIComponent(obj.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
btn.value='<%:Waiting,(executing)...%>';
|
|
||||||
btn.disabled=true;
|
|
||||||
|
|
||||||
|
|
||||||
var targetf=opt.get("target_function").value;
|
|
||||||
if (opt.get("target_disk"))
|
|
||||||
var targetd=opt.get("target_disk").value;
|
|
||||||
else
|
|
||||||
var targetd="/";
|
|
||||||
if (opt.get("format_type"))
|
|
||||||
var eformat=opt.get("format_type").value;
|
|
||||||
else
|
|
||||||
var eformat = "0";
|
|
||||||
if (opt.get("keep_config"))
|
|
||||||
var kconfig = opt.get("keep_config").checked ? 1 : 0;
|
|
||||||
else
|
|
||||||
var kconfig = 0;
|
|
||||||
console.log(eformat);
|
|
||||||
|
|
||||||
XHR.get('<%=url([[admin]], [[system]], [[partexp]], [[partexprun]])%>',{
|
|
||||||
targetf: targetf,
|
|
||||||
kconfig: kconfig,
|
|
||||||
targetd: targetd,
|
|
||||||
eformat: eformat
|
|
||||||
},function(x){});
|
|
||||||
poll_check();
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function poll_check(){
|
|
||||||
var tag = document.getElementById('logview');
|
|
||||||
tag.style.display="block"
|
|
||||||
XHR.poll(3, '<%=url([[admin]], [[system]], [[partexp]], [[check]])%>', null,
|
|
||||||
function(x, data) {
|
|
||||||
var lv = document.getElementById('cbid.logview.1.conf');
|
|
||||||
if (x.responseText && lv) {
|
|
||||||
if (x.responseText=="\u0000"){
|
|
||||||
for(j = 0,len=this.XHR._q.length; j < len; j++) {
|
|
||||||
if (this.XHR._q[j].url == '<%=url([[admin]], [[system]], [[partexp]], [[check]])%>'){
|
|
||||||
this.XHR._q.splice(j,1);
|
|
||||||
checkbtn.disabled = false;
|
|
||||||
checkbtn.value = '<%:Click to execute%>';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (islogreverse){
|
|
||||||
lv.innerHTML = x.responseText.split('\n').reverse().join('\n')+lv.innerHTML;
|
|
||||||
}else{
|
|
||||||
lv.innerHTML += x.responseText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);}
|
|
||||||
|
|
||||||
|
|
||||||
//]]>
|
|
||||||
</script>
|
|
||||||
<%+cbi/valuefooter%>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<%+cbi/valueheader%>
|
|
||||||
<textarea id="logview.list" class="cbi-input-textarea" style="width: 100%" rows="15" readonly="readonly"></textarea>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
const LOG_URL = '<%=luci.dispatcher.build_url("admin", "system", "partexp","realtime_log")%>';
|
|
||||||
|
|
||||||
XHR.poll(1, LOG_URL, null, (x, d) => {
|
|
||||||
let logview = document.getElementById("logview.list");
|
|
||||||
if (!d.running) {
|
|
||||||
XHR.halt();
|
|
||||||
}
|
|
||||||
logview.value = d.log;
|
|
||||||
logview.scrollTop = logview.scrollHeight;
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<%+cbi/valuefooter%>
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<%+cbi/valueheader%>
|
|
||||||
<input type="button" class="btn cbi-button cbi-button-apply" id="apply_update_button" value="<%:Clear Log%>" onclick="apply_del_log()"/>
|
|
||||||
<input type="checkbox" name="NAME" value="reverse" onclick="reverselog()" style="vertical-align:middle;height:auto;"><%:Reverse%></input>
|
|
||||||
<textarea id="cbid.logview.1.conf" class="cbi-input-textarea" style="width: 100%;display:inline" data-update="change" rows="32" cols="60" readonly="readonly" > </textarea>
|
|
||||||
|
|
||||||
<script type="text/javascript">//<![CDATA[
|
|
||||||
var islogreverse=false;
|
|
||||||
|
|
||||||
function createAndDownloadFile(fileName,content){
|
|
||||||
var aTag=document.createElement('a');
|
|
||||||
var blob=new Blob([content]);
|
|
||||||
aTag.download=fileName;
|
|
||||||
aTag.href=URL.createObjectURL(blob);
|
|
||||||
aTag.click();
|
|
||||||
URL.revokeObjectURL(blob);
|
|
||||||
}
|
|
||||||
|
|
||||||
function apply_del_log(){
|
|
||||||
XHR.get('<%=url([[admin]],[[system]],[[partexp]],[[dellog]])%>',null,function(x,data){
|
|
||||||
var lv=document.getElementById('cbid.logview.1.conf');
|
|
||||||
lv.innerHTML="";
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
function reverselog(){
|
|
||||||
var lv=document.getElementById('cbid.logview.1.conf');
|
|
||||||
lv.innerHTML=lv.innerHTML.split('\n').reverse().join('\n')
|
|
||||||
if (islogreverse){
|
|
||||||
islogreverse=false;
|
|
||||||
}else{
|
|
||||||
islogreverse=true;
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
XHR.poll(3,'<%=url([[admin]],[[system]],[[partexp]],[[getlog]])%>',null,
|
|
||||||
function(x,data){
|
|
||||||
var lv=document.getElementById('cbid.logview.1.conf');
|
|
||||||
lv.innerHTML=""
|
|
||||||
if (x.responseText && lv){
|
|
||||||
if (islogreverse){
|
|
||||||
lv.innerHTML=x.responseText.split('\n').reverse().join('\n')+lv.innerHTML;
|
|
||||||
}else{
|
|
||||||
lv.innerHTML+=x.responseText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
//]]>
|
|
||||||
</script>
|
|
||||||
<%+cbi/valuefooter%>
|
|
||||||
199
luci-app-partexp/luci-app-partexp/po/templates/partexp.po
Normal file
199
luci-app-partexp/luci-app-partexp/po/templates/partexp.po
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
# Translation file for partexp.js
|
||||||
|
# Copyright (C) 2022-2025 Sirpdboy <herboy2008@gmail.com>
|
||||||
|
# Licensed to the public under the Apache License 2.0
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: partexp\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Chinese (Simplified)\n"
|
||||||
|
"Language: zh_Hans\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
|
"X-Generator: Generated from partexp.js\n"
|
||||||
|
|
||||||
|
#: JavaScript UI strings
|
||||||
|
|
||||||
|
msgid "One click partition expansion mounting tool"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Automatically format and mount the target device partition. If there are multiple partitions, it is recommended to manually delete all partitions before using this tool."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "For specific usage, see:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Select function"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Used to extend to the root directory of EXT4 firmware(Ext4 /)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Expand application space overlay (/overlay)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Used as Docker data disk (/opt)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Normal mount and use by device name(/mnt/x1)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Select the function to be performed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Destination hard disk"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Loading devices..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Select the hard disk device to operate"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Keep configuration"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Tick means to retain the settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Format system type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No formatting required"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Linux system partition(EXT4)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Large capacity storage devices(Btrfs)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Windows system partition(NTFS)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Perform operation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Click to execute"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Starting operation..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Operation Log"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Are you sure you want to execute partition expansion?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Function:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Disk:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Keep config:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Format type:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "This operation may take several minutes."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Executing..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Operation started successfully"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Operation failed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Failed to start operation:"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Unknown error"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Operation in progress..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Operation in progress..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Operation in progress..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Extend to root directory"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Expand overlay"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Docker data disk"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Normal mount"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No formatting"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Operation completed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Getting device information"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Checking partition format"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Checking target device"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Locating target partition"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Checking free space"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Starting operation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Initializing..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Starting"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: Device status messages
|
||||||
|
msgid "Loading device list..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "No devices found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Failed to load devices"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Please select a target disk"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Settings saved"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Failed to save settings"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Loading saved configuration..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Configuration loaded"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Failed to load configuration"
|
||||||
|
msgstr ""
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
msgid ""
|
|
||||||
msgstr ""
|
|
||||||
"Copyright (C) 2022-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-partexp"
|
|
||||||
"This is free software, licensed under the GNU General Public License v3."
|
|
||||||
|
|
||||||
msgid "Partition Expansion"
|
|
||||||
msgstr "分区扩容"
|
|
||||||
|
|
||||||
msgid "One click partition expansion mounting tool"
|
|
||||||
msgstr "一键分区扩容挂载工具"
|
|
||||||
|
|
||||||
msgid "Automatically format and mount the target device partition. If there are multiple partitions, it is recommended to manually delete all partitions before using this tool.<br/>For specific usage, see:"
|
|
||||||
msgstr "自动对目标设备分区格式化挂载,如果有多分区建议手动删除所有分区再使用本工具.<br/>使用说明见:"
|
|
||||||
|
|
||||||
msgid "Waiting,(executing)..."
|
|
||||||
msgstr "稍等,努力执行中"
|
|
||||||
|
|
||||||
msgid "Expand application space overlay (/overlay)"
|
|
||||||
msgstr "用于overlay软件空间 (/overlay)"
|
|
||||||
|
|
||||||
msgid "Used to extend to the root directory of EXT4 firmware(Ext4 /)"
|
|
||||||
msgstr "用于扩展为EXT4固件根目录(Ext4 /)"
|
|
||||||
|
|
||||||
msgid "Used as Docker data disk (/opt)"
|
|
||||||
msgstr "用作Docker数据盘 (/opt)"
|
|
||||||
|
|
||||||
msgid "Normal mount and use by device name(/mnt/x1)"
|
|
||||||
msgstr "按设备名普通挂载使用(/mnt/x1)"
|
|
||||||
|
|
||||||
msgid "Soft chain partition expansion(/overlay)"
|
|
||||||
msgstr "分区软链扩容(/overlay)"
|
|
||||||
|
|
||||||
msgid "Destination hard disk"
|
|
||||||
msgstr "目标硬盘"
|
|
||||||
|
|
||||||
msgid "Keep configuration"
|
|
||||||
msgstr "保留配置"
|
|
||||||
|
|
||||||
msgid "Format system type"
|
|
||||||
msgstr "格式化系统类型"
|
|
||||||
|
|
||||||
msgid "No formatting required"
|
|
||||||
msgstr "不需要格式化"
|
|
||||||
|
|
||||||
msgid "Linux system partition(EXT4)"
|
|
||||||
msgstr "Linux系统分区(EXT4)"
|
|
||||||
|
|
||||||
msgid "Large capacity storage devices(Btrfs)"
|
|
||||||
msgstr "大容量存储设备(Btrfs)"
|
|
||||||
|
|
||||||
msgid "Windows system partition(NTFS)"
|
|
||||||
msgstr "Windows系统分区(NTFS)"
|
|
||||||
|
|
||||||
msgid "Select the hard disk device to operate"
|
|
||||||
msgstr "选择需要操作的硬盘设备"
|
|
||||||
|
|
||||||
msgid "Select function"
|
|
||||||
msgstr "选择功能"
|
|
||||||
|
|
||||||
msgid "Select the function to be performed"
|
|
||||||
msgstr "选择要执行的功能"
|
|
||||||
|
|
||||||
msgid "Click to execute"
|
|
||||||
msgstr "点击执行"
|
|
||||||
|
|
||||||
msgid "Perform operation"
|
|
||||||
msgstr "执行操作"
|
|
||||||
|
|
||||||
msgid "To make the operation effective, the device will restart. Are you sure to execute?"
|
|
||||||
msgstr "警告:操作一旦确定无法取消,设备将会重启,是否确定执行?"
|
|
||||||
|
|
||||||
msgid "Operation in progress, please wait..."
|
|
||||||
msgstr "操作执行中,请稍候..."
|
|
||||||
|
|
||||||
msgid "After operation, restart the machine, please wait..."
|
|
||||||
msgstr "操作完毕,机器重启,请稍候..."
|
|
||||||
|
|
||||||
msgid "Please delete the partition or share and try again"
|
|
||||||
msgstr "错误,请检查是否有足够空间或是共享使用中。"
|
|
||||||
|
|
||||||
msgid "Restart the device to take effect. Confirm whether to continue?"
|
|
||||||
msgstr "重启设备操作才生效,确定是否继续执行?"
|
|
||||||
|
|
||||||
msgid "Operation execution complete"
|
|
||||||
msgstr "操作执行完毕"
|
|
||||||
|
|
||||||
msgid "Ticking indicates formatting"
|
|
||||||
msgstr "打勾选择表示格式化"
|
|
||||||
|
|
||||||
msgid "Tick means to retain the settings"
|
|
||||||
msgstr "打勾选择表示保留设置"
|
|
||||||
|
|
||||||
msgid "reverse"
|
|
||||||
msgstr "逆序"
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,46 +1,72 @@
|
|||||||
|
# Translation file for partexp.js
|
||||||
|
# Copyright (C) 2022-2025 Sirpdboy <herboy2008@gmail.com>
|
||||||
|
# Licensed to the public under the Apache License 2.0
|
||||||
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Copyright (C) 2022-2025 sirpdboy herboy2008@gmail.com https://github.com/sirpdboy/luci-app-partexp"
|
"Project-Id-Version: partexp\n"
|
||||||
"This is free software, licensed under the GNU General Public License v3."
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"Last-Translator: \n"
|
||||||
|
"Language-Team: Chinese (Simplified)\n"
|
||||||
|
"Language: zh_Hans\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||||
|
"X-Generator: Generated from partexp.js\n"
|
||||||
|
|
||||||
msgid "Partition Expansion"
|
#: JavaScript UI strings
|
||||||
msgstr "分区扩容"
|
|
||||||
|
|
||||||
msgid "One click partition expansion mounting tool"
|
msgid "One click partition expansion mounting tool"
|
||||||
msgstr "一键分区扩容挂载工具"
|
msgstr "一键分区扩容挂载工具"
|
||||||
|
|
||||||
msgid "Automatically format and mount the target device partition. If there are multiple partitions, it is recommended to manually delete all partitions before using this tool.<br/>For specific usage, see:"
|
msgid "Partition Expansion"
|
||||||
msgstr "自动对目标设备分区格式化挂载,如果有多分区建议手动删除所有分区再使用本工具.<br/>使用说明见:"
|
msgstr "分区扩容"
|
||||||
|
|
||||||
msgid "Waiting,(executing)..."
|
msgid "Automatically format and mount the target device partition. If there are multiple partitions, it is recommended to manually delete all partitions before using this tool."
|
||||||
msgstr "稍等,努力执行中"
|
msgstr "自动格式化并挂载目标设备分区。如果有多个分区,建议在使用此工具前手动删除所有分区。"
|
||||||
|
|
||||||
msgid "Expand application space overlay (/overlay)"
|
msgid "For specific usage, see:"
|
||||||
msgstr "用于overlay软件空间 (/overlay)"
|
msgstr "具体用法请参见:"
|
||||||
|
|
||||||
|
msgid "Select function"
|
||||||
|
msgstr "选择功能"
|
||||||
|
|
||||||
msgid "Used to extend to the root directory of EXT4 firmware(Ext4 /)"
|
msgid "Used to extend to the root directory of EXT4 firmware(Ext4 /)"
|
||||||
msgstr "用于扩展为EXT4固件根目录(Ext4 /)"
|
msgstr "用于扩展到EXT4固件的根目录(Ext4 /)"
|
||||||
|
|
||||||
|
msgid "Expand application space overlay (/overlay)"
|
||||||
|
msgstr "扩展应用空间overlay (/overlay)"
|
||||||
|
|
||||||
msgid "Used as Docker data disk (/opt)"
|
msgid "Used as Docker data disk (/opt)"
|
||||||
msgstr "用作Docker数据盘 (/opt)"
|
msgstr "用作Docker数据盘 (/opt)"
|
||||||
|
|
||||||
msgid "Normal mount and use by device name(/mnt/x1)"
|
msgid "Normal mount and use by device name(/mnt/x1)"
|
||||||
msgstr "按设备名普通挂载使用(/mnt/x1)"
|
msgstr "普通挂载并按设备名称使用(/mnt/x1)"
|
||||||
|
|
||||||
msgid "Soft chain partition expansion(/overlay)"
|
msgid "Select the function to be performed"
|
||||||
msgstr "分区软链扩容(/overlay)"
|
msgstr "选择要执行的功能"
|
||||||
|
|
||||||
msgid "Destination hard disk"
|
msgid "Destination hard disk"
|
||||||
msgstr "目标硬盘"
|
msgstr "目标硬盘"
|
||||||
|
|
||||||
|
msgid "Loading devices..."
|
||||||
|
msgstr "正在加载设备..."
|
||||||
|
|
||||||
|
msgid "Select the hard disk device to operate"
|
||||||
|
msgstr "选择要操作的硬盘设备"
|
||||||
|
|
||||||
msgid "Keep configuration"
|
msgid "Keep configuration"
|
||||||
msgstr "保留配置"
|
msgstr "保留配置"
|
||||||
|
|
||||||
|
msgid "Tick means to retain the settings"
|
||||||
|
msgstr "勾选表示保留设置"
|
||||||
|
|
||||||
msgid "Format system type"
|
msgid "Format system type"
|
||||||
msgstr "格式化系统类型"
|
msgstr "格式化系统类型"
|
||||||
|
|
||||||
msgid "No formatting required"
|
msgid "No formatting required"
|
||||||
msgstr "不需要格式化"
|
msgstr "无需格式化"
|
||||||
|
|
||||||
msgid "Linux system partition(EXT4)"
|
msgid "Linux system partition(EXT4)"
|
||||||
msgstr "Linux系统分区(EXT4)"
|
msgstr "Linux系统分区(EXT4)"
|
||||||
@@ -51,46 +77,126 @@ msgstr "大容量存储设备(Btrfs)"
|
|||||||
msgid "Windows system partition(NTFS)"
|
msgid "Windows system partition(NTFS)"
|
||||||
msgstr "Windows系统分区(NTFS)"
|
msgstr "Windows系统分区(NTFS)"
|
||||||
|
|
||||||
msgid "Select the hard disk device to operate"
|
msgid "Perform operation"
|
||||||
msgstr "选择需要操作的硬盘设备"
|
msgstr "执行操作"
|
||||||
|
|
||||||
msgid "Select function"
|
|
||||||
msgstr "选择功能"
|
|
||||||
|
|
||||||
msgid "Select the function to be performed"
|
|
||||||
msgstr "选择要执行的功能"
|
|
||||||
|
|
||||||
msgid "Click to execute"
|
msgid "Click to execute"
|
||||||
msgstr "点击执行"
|
msgstr "点击执行"
|
||||||
|
|
||||||
msgid "Perform operation"
|
msgid "Starting operation..."
|
||||||
msgstr "执行操作"
|
msgstr "正在启动操作..."
|
||||||
|
|
||||||
msgid "To make the operation effective, the device will restart. Are you sure to execute?"
|
msgid "Operation Log"
|
||||||
msgstr "警告:操作一旦确定无法取消,设备将会重启,是否确定执行?"
|
msgstr "操作日志"
|
||||||
|
|
||||||
msgid "Operation in progress, please wait..."
|
msgid "Are you sure you want to execute partition expansion?"
|
||||||
msgstr "操作执行中,请稍候..."
|
msgstr "确定要执行分区扩容吗?"
|
||||||
|
|
||||||
msgid "After operation, restart the machine, please wait..."
|
msgid "Function:"
|
||||||
msgstr "操作完毕,机器重启,请稍候..."
|
msgstr "功能:"
|
||||||
|
|
||||||
msgid "Please delete the partition or share and try again"
|
msgid "Disk:"
|
||||||
msgstr "错误,请检查是否有足够空间或是共享使用中。"
|
msgstr "磁盘:"
|
||||||
|
|
||||||
msgid "Restart the device to take effect. Confirm whether to continue?"
|
msgid "Keep config:"
|
||||||
msgstr "重启设备操作才生效,确定是否继续执行?"
|
msgstr "保留配置:"
|
||||||
|
|
||||||
msgid "Operation execution complete"
|
msgid "Format type:"
|
||||||
msgstr "操作执行完毕"
|
msgstr "格式化类型:"
|
||||||
|
|
||||||
msgid "Ticking indicates formatting"
|
msgid "This operation may take several minutes."
|
||||||
msgstr "打勾选择表示格式化"
|
msgstr "此操作可能需要几分钟时间。"
|
||||||
|
|
||||||
msgid "Tick means to retain the settings"
|
msgid "Executing..."
|
||||||
msgstr "打勾选择表示保留设置"
|
msgstr "正在执行..."
|
||||||
|
|
||||||
msgid "reverse"
|
msgid "Operation started successfully"
|
||||||
msgstr "逆序"
|
msgstr "操作启动成功"
|
||||||
|
|
||||||
|
msgid "Operation failed"
|
||||||
|
msgstr "操作失败"
|
||||||
|
|
||||||
|
msgid "Failed to start operation:"
|
||||||
|
msgstr "启动操作失败:"
|
||||||
|
|
||||||
|
msgid "Unknown error"
|
||||||
|
msgstr "未知错误"
|
||||||
|
|
||||||
|
msgid "Operation in progress..."
|
||||||
|
msgstr "操作正在进行中..."
|
||||||
|
|
||||||
|
msgid "Operation in progress..."
|
||||||
|
msgstr "操作正在进行中..."
|
||||||
|
|
||||||
|
msgid "Operation in progress..."
|
||||||
|
msgstr "操作正在进行中..."
|
||||||
|
|
||||||
|
msgid "Extend to root directory"
|
||||||
|
msgstr "扩展到根目录"
|
||||||
|
|
||||||
|
msgid "Expand overlay"
|
||||||
|
msgstr "扩展overlay"
|
||||||
|
|
||||||
|
msgid "Docker data disk"
|
||||||
|
msgstr "Docker数据盘"
|
||||||
|
|
||||||
|
msgid "Normal mount"
|
||||||
|
msgstr "普通挂载"
|
||||||
|
|
||||||
|
msgid "No formatting"
|
||||||
|
msgstr "不格式化"
|
||||||
|
|
||||||
|
msgid "Operation completed"
|
||||||
|
msgstr "操作完成"
|
||||||
|
|
||||||
|
msgid "Getting device information"
|
||||||
|
msgstr "正在获取设备信息"
|
||||||
|
|
||||||
|
msgid "Checking partition format"
|
||||||
|
msgstr "正在检查分区格式"
|
||||||
|
|
||||||
|
msgid "Checking target device"
|
||||||
|
msgstr "正在检查目标设备"
|
||||||
|
|
||||||
|
msgid "Locating target partition"
|
||||||
|
msgstr "正在定位目标分区"
|
||||||
|
|
||||||
|
msgid "Checking free space"
|
||||||
|
msgstr "正在检查可用空间"
|
||||||
|
|
||||||
|
msgid "Starting operation"
|
||||||
|
msgstr "正在开始操作"
|
||||||
|
|
||||||
|
msgid "Initializing..."
|
||||||
|
msgstr "正在初始化..."
|
||||||
|
|
||||||
|
msgid "Starting"
|
||||||
|
msgstr "开始"
|
||||||
|
|
||||||
|
#: Device status messages
|
||||||
|
msgid "Loading device list..."
|
||||||
|
msgstr "正在加载设备列表..."
|
||||||
|
|
||||||
|
msgid "No devices found"
|
||||||
|
msgstr "未找到设备"
|
||||||
|
|
||||||
|
msgid "Failed to load devices"
|
||||||
|
msgstr "加载设备失败"
|
||||||
|
|
||||||
|
msgid "Please select a target disk"
|
||||||
|
msgstr "请选择目标磁盘"
|
||||||
|
|
||||||
|
msgid "Settings saved"
|
||||||
|
msgstr "设置已保存"
|
||||||
|
|
||||||
|
msgid "Failed to save settings"
|
||||||
|
msgstr "保存设置失败"
|
||||||
|
|
||||||
|
msgid "Loading saved configuration..."
|
||||||
|
msgstr "正在加载保存的配置..."
|
||||||
|
|
||||||
|
msgid "Configuration loaded"
|
||||||
|
msgstr "配置已加载"
|
||||||
|
|
||||||
|
msgid "Failed to load configuration"
|
||||||
|
msgstr "加载配置失败"
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
config global 'global'
|
config global 'global'
|
||||||
option target_function '/opt'
|
option target_function '/opt'
|
||||||
option target_disk ''
|
option target_disk ''
|
||||||
option keep_config '0'
|
option keep_config '0'
|
||||||
option format_type '0'
|
option format_type '0'
|
||||||
|
|||||||
@@ -1,751 +0,0 @@
|
|||||||
#!/bin/sh /etc/rc.common
|
|
||||||
|
|
||||||
#
|
|
||||||
# Copyright (C) 2021-2025 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/luci-app-partexp
|
|
||||||
|
|
||||||
# This is free software, licensed under the Apache License, Version 2.0 .
|
|
||||||
#
|
|
||||||
|
|
||||||
START=99
|
|
||||||
USE_PROCD=1
|
|
||||||
|
|
||||||
EXTRA_COMMANDS="autopart"
|
|
||||||
|
|
||||||
CONFIG="taskplan"
|
|
||||||
LOCK=/var/lock/$CONFIG.lock
|
|
||||||
LOGD=/var/$CONFIG
|
|
||||||
LOGDIR=/etc/$CONFIG
|
|
||||||
LOG=$LOGDIR/bk$CONFIG.log
|
|
||||||
|
|
||||||
[ -d "$LOGDIR" ] || mkdir -p $LOGDIR
|
|
||||||
[ -d "$LOGD" ] || mkdir -p $LOGD
|
|
||||||
|
|
||||||
limit_log() {
|
|
||||||
local logf=$1
|
|
||||||
[ ! -f "$logf" ] && return
|
|
||||||
local sc=100
|
|
||||||
[ -n "$2" ] && sc=$2
|
|
||||||
local count=$(grep -c "" $logf)
|
|
||||||
if [ $count -gt $sc ];then
|
|
||||||
let count=count-$sc
|
|
||||||
sed -i "1,$count d" $logf
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
init_env() {
|
|
||||||
[ ! -f "$LOG" ] && echo " " > $LOG
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
gen_log()(
|
|
||||||
log "--自动分区扩展挂载开始执行-- " | tee -a $LOG
|
|
||||||
)
|
|
||||||
|
|
||||||
log(){
|
|
||||||
echo -e " $(date +'%Y-%m-%d %H:%M:%S') $*" | tee -a $LOG
|
|
||||||
}
|
|
||||||
|
|
||||||
# 检查硬盘是否已挂载
|
|
||||||
is_disk_mounted() {
|
|
||||||
DISK=$1
|
|
||||||
if mount | grep -q "$DISK "; then
|
|
||||||
return 0 # 已挂载
|
|
||||||
else
|
|
||||||
return 1 # 未挂载
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
mount_device() {
|
|
||||||
local DEVICE=$1
|
|
||||||
local MOUNT_POINT=$2
|
|
||||||
local TYPE=$3
|
|
||||||
# 检查设备是否存在
|
|
||||||
if [ ! -e "$DEVICE" ]; then
|
|
||||||
log "设备 $DEVICE 不存在"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 检查挂载点是否存在
|
|
||||||
if [ ! -d "$MOUNT_POINT" ]; then
|
|
||||||
log "挂载点 $MOUNT_POINT 不存在,正在创建..."
|
|
||||||
mkdir -p "$MOUNT_POINT"
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
log "无法创建挂载点 $MOUNT_POINT"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 检查设备是否已挂载
|
|
||||||
if mount | grep -q "$DEVICE"; then
|
|
||||||
log "设备 $DEVICE 已挂载到其他位置"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if mount | grep -q "$MOUNT_POINT"; then
|
|
||||||
log "挂载点 $MOUNT_POINT 已被其他设备占用"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 挂载设备
|
|
||||||
log "正在挂载 $DEVICE 到 $MOUNT_POINT..."
|
|
||||||
# mount "$DEVICE" "$MOUNT_POINT"
|
|
||||||
|
|
||||||
mount $TYPE "$DEVICE" "$MOUNT_POINT" >/dev/null 2> /dev/null
|
|
||||||
# 检查挂载是否成功
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
log "挂载成功: $DEVICE -> $MOUNT_POINT"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
log "挂载失败: $DEVICE -> $MOUNT_POINT"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# 取消硬盘挂载
|
|
||||||
umount_disk() {
|
|
||||||
DISK=$1
|
|
||||||
MOUNT=''
|
|
||||||
eval $(block info "$DISK" | grep -o -e "MOUNT=\S*")
|
|
||||||
if [ "$MOUNT" ]; then
|
|
||||||
umount $DISK 2>/dev/null
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
log "取消挂载成功:$DISK"
|
|
||||||
else
|
|
||||||
log "取消挂载失败:$DISK"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log "设备/dev/$DISK未挂载"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# 从 block info 中提取指定字段的值
|
|
||||||
get_block() {
|
|
||||||
local DISK=$1
|
|
||||||
local TYPE=$2
|
|
||||||
local value
|
|
||||||
value=`mount | grep $DISK |awk -F $TYPE '{print $2}' |awk '{print $1}' | head -1`
|
|
||||||
# value=$(block info "/dev/$DISK" | grep -o -e "$TYPE=\S*" | cut -d\" -f2)
|
|
||||||
echo $value
|
|
||||||
}
|
|
||||||
|
|
||||||
# 检查是否有共享挂载(如 Samba 或 NFS)
|
|
||||||
check_shared_mount() {
|
|
||||||
DISK=$1
|
|
||||||
if [ -f /etc/config/samba ]; then
|
|
||||||
SHARED=$(grep -q "/dev/$DISK" /etc/config/samba)
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
log "检测到 Samba 共享挂载: /dev/$DISK"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# 检查是否有 NFS 共享
|
|
||||||
if [ -f /etc/exports ]; then
|
|
||||||
SHARED=$(grep -q "/dev/$DISK" /etc/exports)
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
log "检测到 NFS 共享挂载: /dev/$DISK"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
usamba(){
|
|
||||||
s=$1
|
|
||||||
[ -e "/etc/config/$s" ] && {
|
|
||||||
msum=$(grep -c "config sambashare" /etc/config/$s)
|
|
||||||
for i in $(seq 0 $((msum)));do
|
|
||||||
pdev=`uci -q get $s.@sambashare[$i].path `
|
|
||||||
[ "$pdev" = "$2" ] && {
|
|
||||||
uci delete $s.@sambashare[$i]
|
|
||||||
uci commit $s
|
|
||||||
log "分区/dev/$b被挂载$MOUNT共享使用,删除$s共享成功!"
|
|
||||||
sleep 5
|
|
||||||
}
|
|
||||||
done
|
|
||||||
}
|
|
||||||
# 取消 Samba 共享
|
|
||||||
if [ -f /etc/config/$s ]; then
|
|
||||||
sed -i "/\/dev\/$b/d" /etc/config/$s
|
|
||||||
/etc/init.d/$s restart
|
|
||||||
fi
|
|
||||||
# 取消 NFS 共享
|
|
||||||
if [ -f /etc/exports ]; then
|
|
||||||
sed -i "/\/dev\/$b/d" /etc/exports
|
|
||||||
/etc/init.d/nfs restart
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
is_disk_partitioned() {
|
|
||||||
PARTITION_COUNT=$(fdisk -l /dev/$1 2>/dev/null | grep -E "^/dev/$2" | wc -l)
|
|
||||||
if [[ "$PARTITION_COUNT" -gt 0 ]]; then
|
|
||||||
echo 1
|
|
||||||
else
|
|
||||||
echo 0
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
partednew(){
|
|
||||||
DISK=$1
|
|
||||||
parted -s /dev/$DISK mklabel gpt
|
|
||||||
parted -s /dev/$DISK mkpart primary ext4 1MiB -1
|
|
||||||
}
|
|
||||||
|
|
||||||
fdisknew(){
|
|
||||||
echo -e "n\np\n\n\n\nw" | fdisk /dev/$1 >/dev/null 2> /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
fdisksave(){
|
|
||||||
echo -e "n\w" | fdisk /dev/$1 >/dev/null 2> /dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
# 格式化磁盘函数 DISK=/dev/sda1 ;TYPE=btrfs
|
|
||||||
format_disk() {
|
|
||||||
local DISK=$1
|
|
||||||
local TYPE=$2
|
|
||||||
[[ $TYPE == '0' || $TYPE == '' ]] && TYPE="ext4"
|
|
||||||
log "正在格式化 $DISK "
|
|
||||||
mkfs.$TYPE -F "$DISK" >/dev/null 2>/dev/null
|
|
||||||
if [ $? -eq 0 ]; then
|
|
||||||
log "格式化 $TYPE 成功 $DISK"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
log "格式化 $TYPE 失败 $DISK"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
fdiskB(){
|
|
||||||
|
|
||||||
a=$1
|
|
||||||
b=$1$2
|
|
||||||
log "开始检测目标$a信息"
|
|
||||||
log "检测/dev/$a是否需要分区和格式化$format_type"
|
|
||||||
block detect > /etc/config/fstab
|
|
||||||
uci -q set fstab.@global[0].anon_mount='0'
|
|
||||||
uci -q set fstab.@global[0].auto_mount='0'
|
|
||||||
uci commit fstab
|
|
||||||
if [ $target_function = '/opt' ] ;then
|
|
||||||
/etc/init.d/dockerd stop >/dev/null 2> /dev/null
|
|
||||||
amount=`mount |grep /opt | awk '{print $1}'`
|
|
||||||
if [ -n "$amount" ] ;then
|
|
||||||
umount $amount >/dev/null 2> /dev/null
|
|
||||||
log "取消/opt之前的挂载$amount成功!"
|
|
||||||
fi
|
|
||||||
for OPT in $(mount |grep /opt | awk '{print $3}');do
|
|
||||||
umount $OPT >/dev/null 2> /dev/null
|
|
||||||
log "取消/opt之前的挂载$OPT成功!"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
[ -d "/mnt/$b" ] || mkdir -p /mnt/$b
|
|
||||||
if is_disk_mounted "/dev/$b"; then
|
|
||||||
log "设备 /dev/$b 已挂载,尝试取消挂载..."
|
|
||||||
if check_shared_mount $b; then
|
|
||||||
usamba samba4 $MOUNT
|
|
||||||
usamba samba $MOUNT
|
|
||||||
sleep 5
|
|
||||||
fi
|
|
||||||
umount_disk "/dev/$b"
|
|
||||||
[ $? -ne 0 ] || umount_disk "/mnt/$b"
|
|
||||||
else
|
|
||||||
log "设备/dev/$b未挂载"
|
|
||||||
isfdisk=0
|
|
||||||
isP=$(is_disk_partitioned $a $b)
|
|
||||||
if [ "$isP" = '0' ] ;then
|
|
||||||
fdisksave $a
|
|
||||||
fdisknew $a
|
|
||||||
sleep 2
|
|
||||||
isfdisk=1
|
|
||||||
fi
|
|
||||||
isP=$(is_disk_partitioned $a $b)
|
|
||||||
if [[ "$isP" = '1' && "$isfdisk" = 1 ]] ;then
|
|
||||||
log "分区$b建立成功!"
|
|
||||||
elif [[ "$isP" = '1' && "$isfdisk" = 0 ]] ;then
|
|
||||||
log "检测目标分区$b已存在."
|
|
||||||
else
|
|
||||||
log "分区$b建立失败,请检查$b硬盘空间!"
|
|
||||||
expquit 1
|
|
||||||
fi
|
|
||||||
sleep 1
|
|
||||||
fi
|
|
||||||
if is_disk_mounted "/dev/$b"; then
|
|
||||||
umount /dev/$b >/dev/null 2> /dev/null
|
|
||||||
[ $? -ne 0 ] && block umount /dev/$b >/dev/null 2> /dev/null
|
|
||||||
fi
|
|
||||||
if [[ "$target_function" = "/" || "$target_function" = "/overlay" ]] ; then
|
|
||||||
format_disk "/dev/$b" $format_type
|
|
||||||
elif [[ "$format_type" != "0" || "$isfdisk" = "1" ]] ; then
|
|
||||||
format_disk "/dev/$b" $format_type
|
|
||||||
else
|
|
||||||
log "设备/dev/$b如果未格式化,可能无法正常使用."
|
|
||||||
fi
|
|
||||||
|
|
||||||
TYPE='';eval $(blkid "/dev/$b" | grep -o -e "TYPE=\S*")
|
|
||||||
|
|
||||||
log "检测设备/dev/$b分区$TYPE格式!"
|
|
||||||
|
|
||||||
if [ "$TYPE" = "ntfs" ];then
|
|
||||||
if [ `which ntfs-3g ` ] ;then
|
|
||||||
if is_disk_mounted "/mnt/$b" ;then
|
|
||||||
mount_device /dev/$b /mnt/$b "-t ntfs-3g"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
if is_disk_mounted "/mnt/$b" ;then
|
|
||||||
mount_device /dev/$b /mnt/$b "-t ntfs3"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
mount /dev/$b /mnt/$b >/dev/null 2> /dev/null
|
|
||||||
fi
|
|
||||||
UUID='';eval $(block info /dev/$b | grep -o -e "UUID=\S*")
|
|
||||||
if [ ! "$UUID" ] ; then
|
|
||||||
log "获取/dev/$b设备UUID信息失败!"
|
|
||||||
expquit 1
|
|
||||||
else
|
|
||||||
log "获取/dev/$b设备UUID信息:$UUID成功"
|
|
||||||
fi
|
|
||||||
case "$target_function" in
|
|
||||||
|
|
||||||
"/overlay")
|
|
||||||
if [ "$keep_config" = "1" ] ; then
|
|
||||||
# cp -a -f /overlay/* /mnt/$b/ || cp -a -f /rom/overlay/* /mnt/$b/
|
|
||||||
tar -C /overlay -cvf - . | tar -C /mnt/$b/ -xf - || tar -C /rom/overlay -cvf - . | tar -C /mnt/$b/ -xf -
|
|
||||||
umount /dev/$b >/dev/null 2> /dev/null
|
|
||||||
[ $? -ne 0 ] && umount /mnt/$b >/dev/null 2> /dev/null
|
|
||||||
[ $? -ne 0 ] && block umount /dev/$b >/dev/null 2> /dev/null
|
|
||||||
block detect > /etc/config/fstab
|
|
||||||
OVERLAY=`uci -q get fstab.@mount[0].target `
|
|
||||||
if [[ "$OVERLAY" = "/overlay" || "$OVERLAY" = "/dev/loop0" ]] ;then
|
|
||||||
uci -q set fstab.@mount[0].uuid="${UUID}"
|
|
||||||
uci -q set fstab.@mount[0].target='/overlay'
|
|
||||||
uci -q set fstab.@mount[0].enabled='0'
|
|
||||||
fi
|
|
||||||
msum=$(grep -c "'mount'" /etc/config/fstab)
|
|
||||||
for i in $(seq 0 $((msum-1)))
|
|
||||||
do
|
|
||||||
zuuid=`uci -q get fstab.@mount[$i].uuid `
|
|
||||||
[ $? -ne 0 ] && break
|
|
||||||
if [ "$zuuid" = "$UUID" ] ; then
|
|
||||||
uci -q set fstab.@mount[$i].target="/overlay"
|
|
||||||
uci -q set fstab.@mount[$i].enabled='1'
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
uci set fstab.@global[0].delay_root="15"
|
|
||||||
uci commit fstab
|
|
||||||
log "保留数据overlay扩展/dev/$b成功!"
|
|
||||||
eval $(block info /dev/$b | grep -o -e "MOUNT=\S*")
|
|
||||||
echo $MOUNT $a>> /etc/partexppath
|
|
||||||
sleep 3
|
|
||||||
log "设备重启才能生效"
|
|
||||||
expquit 2
|
|
||||||
else
|
|
||||||
umount /dev/$b >/dev/null 2> /dev/null
|
|
||||||
[ $? -ne 0 ] && umount /mnt/$b >/dev/null 2> /dev/null
|
|
||||||
[ $? -ne 0 ] && block umount /dev/$b >/dev/null 2> /dev/null
|
|
||||||
block detect > /etc/config/fstab
|
|
||||||
OVERLAY=`uci -q get fstab.@mount[0].target `
|
|
||||||
if [[ "$OVERLAY" = "/overlay" || "$OVERLAY" = "/dev/loop0" ]] ;then
|
|
||||||
uci -q set fstab.@mount[0].uuid="${UUID}"
|
|
||||||
uci -q set fstab.@mount[0].target='/overlay'
|
|
||||||
uci -q set fstab.@mount[0].enabled='0'
|
|
||||||
fi
|
|
||||||
msum=$(grep -c "'mount'" /etc/config/fstab)
|
|
||||||
for i in $(seq 0 $((msum-1)))
|
|
||||||
do
|
|
||||||
zuuid=`uci -q get fstab.@mount[$i].uuid `
|
|
||||||
[ $? -ne 0 ] && break
|
|
||||||
if [ "$zuuid" = "$UUID" ] ; then
|
|
||||||
uci -q set fstab.@mount[$i].target="/overlay"
|
|
||||||
uci -q set fstab.@mount[$i].enabled='1'
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
uci set fstab.@global[0].delay_root="15"
|
|
||||||
uci commit fstab
|
|
||||||
log "不保留数据overlay扩展/dev/$b成功!"
|
|
||||||
eval $(block info /dev/$b | grep -o -e "MOUNT=\S*")
|
|
||||||
echo $MOUNT $a>> /etc/partexppath
|
|
||||||
sleep 3
|
|
||||||
log "设备重启才能生效"
|
|
||||||
expquit 2
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
"/opt")
|
|
||||||
umount /dev/$b >/dev/null 2> /dev/null
|
|
||||||
[ $? -ne 0 ] && umount /mnt/$b >/dev/null 2> /dev/null
|
|
||||||
[ $? -ne 0 ] && block umount /dev/$b >/dev/null 2> /dev/null
|
|
||||||
block detect > /etc/config/fstab
|
|
||||||
mkdir -p $target_function
|
|
||||||
msum=$(grep -c "'mount'" /etc/config/fstab)
|
|
||||||
mount_device /dev/$b "$target_function"
|
|
||||||
for i in $(seq 0 $((msum-1)))
|
|
||||||
do
|
|
||||||
zuuid=`uci -q get fstab.@mount[$i].uuid `
|
|
||||||
[ $? -ne 0 ] && break
|
|
||||||
if [ "$zuuid" = "$UUID" ] ; then
|
|
||||||
uci -q set fstab.@mount[$i].target="$target_function"
|
|
||||||
uci -q set fstab.@mount[$i].enabled='1'
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
uci commit fstab
|
|
||||||
# ln -sf /mnt/$b /overlay
|
|
||||||
if is_disk_mounted "/opt"; then
|
|
||||||
log "/dev/$b分区扩容和挂载到$target_function成功!"
|
|
||||||
eval $(block info /dev/$b | grep -o -e "MOUNT=\S*")
|
|
||||||
echo $MOUNT $a>> /etc/partexppath
|
|
||||||
log "如果没生效,请重启设备"
|
|
||||||
expquit 2
|
|
||||||
else
|
|
||||||
log "/dev/$b分区扩容和挂载到$target_function失败!"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
"/")
|
|
||||||
|
|
||||||
|
|
||||||
ROOTBLK="$(readlink -f /sys/dev/block/"$(awk '$9="/dev/root"{print $3}' /proc/self/mountinfo)")"
|
|
||||||
[ -z "$ROOTBLK" ] && { log "错误:无法获取根分区块设备"; expquit 1; }
|
|
||||||
ROOTDISK="/dev/$(basename "${ROOTBLK%/}")"
|
|
||||||
FSTYPE=$(blkid -o value -s TYPE "$ROOTDISK" 2>/dev/null)
|
|
||||||
if [[ "$FSTYPE" != "squashfs" && -n "$FSTYPE" ]] ; then
|
|
||||||
if [ $target_function = '/' ] ;then
|
|
||||||
FREE_SPACE=$(check_free_space $(basename $DISK))
|
|
||||||
log "目标盘 $ROOT_PART $FSTYPE有剩余空间: $FREE_SPACE Gb"
|
|
||||||
if [[ "$FREE_SPACE" -gt 2 ]]; then
|
|
||||||
rootpt_resize
|
|
||||||
expquit 2
|
|
||||||
else
|
|
||||||
log "目标盘 $SYSTEM_DISK $FSTYPE没有足够的剩余空间!"
|
|
||||||
expquit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if [ $target_function = '/overlay' ] ;then
|
|
||||||
FREE_SPACE=$(check_free_space $(basename $DISK))
|
|
||||||
log "目标盘 $ROOT_PART $FSTYPE有剩余空间: $FREE_SPACE Gb"
|
|
||||||
if [[ "$FREE_SPACE" -gt 2 ]]; then
|
|
||||||
rootfs_resize
|
|
||||||
eval $(block info /dev/$b | grep -o -e "MOUNT=\S*")
|
|
||||||
echo $MOUNT $a>> /etc/partexppath
|
|
||||||
expquit 2
|
|
||||||
else
|
|
||||||
log "目标盘 $SYSTEM_DISK $FSTYPE没有足够的剩余空间!"
|
|
||||||
expquit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
log "目标硬盘不支持/根分区扩展!请换EXT4固件!"
|
|
||||||
fi
|
|
||||||
sleep 3
|
|
||||||
expquit 2
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
umount /dev/$b >/dev/null 2> /dev/null
|
|
||||||
[ $? -ne 0 ] && umount /mnt/$b >/dev/null 2> /dev/null
|
|
||||||
[ $? -ne 0 ] && block umount /dev/$b >/dev/null 2> /dev/null
|
|
||||||
block detect > /etc/config/fstab
|
|
||||||
mkdir -p $target_function
|
|
||||||
msum=$(grep -c "'mount'" /etc/config/fstab)
|
|
||||||
mount_device /dev/$b /mnt/$b
|
|
||||||
for i in $(seq 0 $((msum-1)))
|
|
||||||
do
|
|
||||||
zuuid=`uci -q get fstab.@mount[$i].uuid `
|
|
||||||
[ $? -ne 0 ] && break
|
|
||||||
if [ "$zuuid" = "$UUID" ] ; then
|
|
||||||
uci -q set fstab.@mount[$i].target="/mnt/$b"
|
|
||||||
uci -q set fstab.@mount[$i].enabled='1'
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
uci commit fstab
|
|
||||||
if is_disk_mounted /mnt/$b ; then
|
|
||||||
log "/dev/$b分区扩容和挂载到/mnt/$b成功!"
|
|
||||||
eval $(block info /dev/$b | grep -o -e "MOUNT=\S*")
|
|
||||||
echo $MOUNT $a>> /etc/partexppath
|
|
||||||
log "如果没生效,请重启设备"
|
|
||||||
expquit 2
|
|
||||||
else
|
|
||||||
log "/dev/$b分区扩容和挂载到/mnt/$b失败!"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
get_system_disk() {
|
|
||||||
SYSTEM_DISK=$(df -h | grep boot | awk '{print $1}' | head -1 | sed -E 's/(p?[0-9]+)$//')
|
|
||||||
[ -z ${SYSTEM_DISK} ] && SYSTEM_DISK=$(mount | grep 'on /overlay' | awk '{print $1}' | sed -E 's/(p?[0-9]+)$//' |head -1)
|
|
||||||
echo "$SYSTEM_DISK"
|
|
||||||
ROOT_DISK="/dev/$(basename "${ROOTBLK%/*}")"
|
|
||||||
echo "$ROOT_DISK"
|
|
||||||
}
|
|
||||||
|
|
||||||
get_all_disks() {
|
|
||||||
DISKS=`find /dev -regex '.*/\(sd[a-z]\|mmcblk[0-9]\+\|sata[a-z]\|nvme[0-9]\+n[0-9]\+\|vd[a-z]\)$'`
|
|
||||||
echo "$DISKS"
|
|
||||||
}
|
|
||||||
|
|
||||||
check_part_space() {
|
|
||||||
DISK=$1
|
|
||||||
info=$(lsblk -no SIZE,FSTYPE,MOUNTPOINT "$DISK" | awk '{print $1}')
|
|
||||||
if [ -z "$info" ]; then
|
|
||||||
echo "物理大小: 未知(可能是未格式化的裸分区)"
|
|
||||||
else
|
|
||||||
echo $info |awk -F '.' '{print $1}' | sed 's/[A-Za-z]//g'
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
check_free_space() {
|
|
||||||
DISK=$1
|
|
||||||
PARTED_OUTPUT=$(parted -s /dev/$DISK unit GB print free 2>/dev/null)
|
|
||||||
FREE_SPACE=$(echo "$PARTED_OUTPUT" | grep "Free Space" | awk '{print $3}' )
|
|
||||||
echo $FREE_SPACE |awk -F '.' '{print $1}' | sed 's/[A-Za-z]//g'
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
show_partition_info() {
|
|
||||||
local partition="$1"
|
|
||||||
if [ ! -e "$partition" ]; then
|
|
||||||
echo "错误:分区 $partition 不存在!"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
echo -e "\n=== 分区信息 [$partition] ==="
|
|
||||||
local lsblk_info=$(lsblk -no SIZE,FSTYPE,MOUNTPOINT "$partition" 2>/dev/null)
|
|
||||||
if [ -z "$lsblk_info" ]; then
|
|
||||||
echo "物理大小: 未知(可能是未格式化的裸分区)"
|
|
||||||
else
|
|
||||||
local size=$(echo "$lsblk_info" | awk '{print $1}')
|
|
||||||
local fstype=$(echo "$lsblk_info" | awk '{print $2}')
|
|
||||||
local mountpoint=$(echo "$lsblk_info" | awk '{print $3}')
|
|
||||||
echo "物理大小: $size"
|
|
||||||
echo "文件系统: ${fstype:-未知}"
|
|
||||||
echo "挂载点: ${mountpoint:-未挂载}"
|
|
||||||
fi
|
|
||||||
if df "$partition" &>/dev/null; then
|
|
||||||
local df_info=$(df -h "$partition" | awk 'NR=2 {print $2,$3,$4,$5}')
|
|
||||||
echo -e "\n[已挂载] 空间使用情况:"
|
|
||||||
echo "总容量: $(echo "$df_info" | awk '{print $1}')"
|
|
||||||
echo "已用: $(echo "$df_info" | awk '{print $2}')"
|
|
||||||
echo "剩余: $(echo "$df_info" | awk '{print $3}')"
|
|
||||||
echo "使用率: $(echo "$df_info" | awk '{print $4}')"
|
|
||||||
else
|
|
||||||
echo -e "\n[未挂载] 无法查询使用情况(需先挂载)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
local disk="${partition%[0-9]*}"
|
|
||||||
local part_num="${partition##*[!0-9]}"
|
|
||||||
echo -e "\n分区表信息:"
|
|
||||||
parted -s "$disk" unit MiB print | grep -w "^ $part_num" | awk '{print "起始: " $2 " MiB | 结束: " $3 " MiB | 类型: " $6}'
|
|
||||||
}
|
|
||||||
|
|
||||||
get_next_partition_number() {
|
|
||||||
DISK=$1
|
|
||||||
PARTITIONS=$(fdisk -l /dev/$DISK 2>/dev/null | grep -v boot | grep -E "^/dev/$DISK" | awk '{print $1}' | sed 's/\/dev\/[a-z]*//g' | awk -F '[^0-9]+' '{print $NF}')
|
|
||||||
MAX_PARTITION=$(echo "$PARTITIONS" | sort -n | tail -n 1)
|
|
||||||
NEXT_PARTITION=$(awk -v n="$MAX_PARTITION" 'BEGIN { print n + 1 }')
|
|
||||||
#NEXT_PARTITION=$((MAX_PARTITION + 1))
|
|
||||||
echo "$NEXT_PARTITION"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
get_last_partition_number() {
|
|
||||||
DISK=$1
|
|
||||||
PARTITIONS=$(fdisk -l /dev/$DISK 2>/dev/null | grep -v boot | grep -E "^/dev/$DISK" | awk '{print $1}' | sed 's/\/dev\/[a-z]*//g' | awk -F '[^0-9]+' '{print $NF}')
|
|
||||||
MAX_PARTITION=$(echo "$PARTITIONS" | sort -n | tail -n 1)
|
|
||||||
echo "$MAX_PARTITION"
|
|
||||||
}
|
|
||||||
|
|
||||||
get_partition_number() {
|
|
||||||
DISK=$1
|
|
||||||
PARTITIONS=$(fdisk -l /dev/$DISK 2>/dev/null | grep -v boot | grep -E "^/dev/$DISK" | awk '{print $1}' | sed 's/\/dev\/[a-z]*//g' | wc -l)
|
|
||||||
echo "$PARTITIONS"
|
|
||||||
}
|
|
||||||
|
|
||||||
rootpt_resize()
|
|
||||||
{
|
|
||||||
if [ ! -e /etc/rootpt-resize ] ;then
|
|
||||||
log "--->请稍侯,系统根分区扩展中<---"
|
|
||||||
ROOTBLK="$(readlink -f /sys/dev/block/"$(awk -e '$9="/dev/root"{print $3}' /proc/self/mountinfo)")"
|
|
||||||
ROOTDISK="/dev/$(basename "${ROOTBLK%/}")"
|
|
||||||
ROOTPART="${ROOTBLK##*[^0-9]}"
|
|
||||||
partplace=$(fdisk -l 2>/dev/null | grep "$ROOTDISK" | awk '{print $5}' )
|
|
||||||
log "--->根分区$ROOTDISK:$partplace <---"
|
|
||||||
sleep 3
|
|
||||||
parted -f -s "${ROOTDISK}" resizepart "${ROOTPART}" 100%
|
|
||||||
mount_root done
|
|
||||||
touch /etc/rootpt-resize
|
|
||||||
sleep 3
|
|
||||||
log "--->系统根分区扩展成功!<---"
|
|
||||||
partplace=$(fdisk -l 2>/dev/null | grep "$ROOTDISK" | awk '{print $5}' )
|
|
||||||
log "--->根分区$ROOTDISK扩展后容量:$partplace <---"
|
|
||||||
log "--->如果没生效,请重启设备<---"
|
|
||||||
expquit 2
|
|
||||||
else
|
|
||||||
log "已经扩展过或者挂载分区过,请删除分区或者重置重新操作或者联系作者sirpdboy!"
|
|
||||||
expquit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
rootfs_resize()
|
|
||||||
{
|
|
||||||
if [ ! -e /etc/rootfs-resize ] && [ -e /etc/rootpt-resize ] ;then
|
|
||||||
log "--->请稍侯,系统根分区扩展中<---"
|
|
||||||
ROOTBLK="$(readlink -f /sys/dev/block/"$(awk -e '$9="/dev/root"{print $3}' /proc/self/mountinfo)")"
|
|
||||||
ROOTPART="${ROOTBLK##*[^0-9]}"
|
|
||||||
ROOTDISK="/dev/$(basename "${ROOTBLK%/}")"
|
|
||||||
partplace=$(fdisk -l 2>/dev/null | grep "$ROOTDISK" | awk '{print $5}' )
|
|
||||||
log "--->根分区$ROOTDISK:$partplace <---"
|
|
||||||
sleep 3
|
|
||||||
parted -f -s "${ROOTDISK}" resizepart "${ROOTPART}" 100%
|
|
||||||
mount_root done
|
|
||||||
touch /etc/rootpt-resize
|
|
||||||
sleep 3
|
|
||||||
log "--->系统根分区扩展成功!<---"
|
|
||||||
partplace=$(fdisk -l 2>/dev/null | grep "$ROOTDISK" | awk '{print $5}' )
|
|
||||||
log "--->根分区$ROOTDISK扩展后容量:$partplace <---"
|
|
||||||
log "--->请稍侯,系统overlay扩展中<---"
|
|
||||||
df -h /overlay | awk 'NR=2 {printf " overlay扩展前: 总容量: %s 已用: %s 剩余: %s 使用率: %s", $2, $3, $4, $5}'
|
|
||||||
LOOPDEV="$(awk -e '$5="/overlay"{print $9}' /proc/self/mountinfo)"
|
|
||||||
if [ -z "${LOOPDEV}" ] ; then
|
|
||||||
LOOP_DEV="$(losetup -f)"
|
|
||||||
losetup "${LOOPDEV}" "${ROOTDEV}"
|
|
||||||
fi
|
|
||||||
# eval $(blkid "$LOOPDEV" | grep -o -e "TYPE=\S*")
|
|
||||||
FSTYPE=$(blkid -o value -s TYPE "$LOOPDEV" 2>/dev/null)
|
|
||||||
umount -l /overlay
|
|
||||||
mount -t tmpfs -o size=128M tmpfs /overlay
|
|
||||||
losetup -d /dev/loop0
|
|
||||||
losetup -fP ${ROOTDISK}
|
|
||||||
case "$FSTYPE" in
|
|
||||||
f2fs)
|
|
||||||
umount /overlay || { log "错误:无法卸载 /overlay"; expquit 1; }
|
|
||||||
fsck.f2fs -f "$LOOPDEV"
|
|
||||||
resize.f2fs -f "$LOOPDEV" || { log "错误:f2fs 调整大小失败"; expquit 1; }
|
|
||||||
;;
|
|
||||||
ext4)
|
|
||||||
resize2fs -f "$LOOPDEV" || { log "错误:ext4 调整大小失败"; expquit 1; }
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
log "--->分区格式 $FSTYPE 不识别,overlay 扩展失败!<---"
|
|
||||||
expquit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
mount_root done
|
|
||||||
touch /etc/rootfs-resize
|
|
||||||
sleep 3
|
|
||||||
log "--->系统overlay扩展成功!<---"
|
|
||||||
df -h /overlay | awk 'NR=2 {printf " overlay扩展后: 总容量: %s 已用: %s 剩余: %s 使用率: %s", $2, $3, $4, $5}'
|
|
||||||
log "--->如果没生效,请重启设备<---"
|
|
||||||
expquit 2
|
|
||||||
else
|
|
||||||
log "已经扩展过或者挂载分区过,请删除分区或者重置重新操作或者联系作者sirpdboy!"
|
|
||||||
expquit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
get_config() {
|
|
||||||
config_get target_function $1 target_function 1
|
|
||||||
config_get target_disk $1 target_disk 1
|
|
||||||
config_get_bool keep_config $1 keep_config 1
|
|
||||||
config_get format_type $1 format_type
|
|
||||||
}
|
|
||||||
|
|
||||||
autopart() {
|
|
||||||
config_load partexp
|
|
||||||
config_foreach get_config global
|
|
||||||
touch $LOCK
|
|
||||||
init_env
|
|
||||||
gen_log
|
|
||||||
uci -q set fstab.@global[0].anon_mount='0'
|
|
||||||
uci -q set fstab.@global[0].auto_mount='0'
|
|
||||||
uci commit fstab
|
|
||||||
[ -e "/etc/config/dockerd" ] && /etc/init.d/dockerd stop >/dev/null 2> /dev/null
|
|
||||||
DISK=$target_disk
|
|
||||||
NEXTPART=1
|
|
||||||
DISKSALL=$(get_all_disks)
|
|
||||||
DISK_COUNT=$(echo "$DISKSALL" | wc -l)
|
|
||||||
log "系统中检测到的硬盘数量: $DISK_COUNT"
|
|
||||||
log "硬盘信息列表:" $DISKSALL
|
|
||||||
SYSTEM_DISK=$(get_system_disk)
|
|
||||||
log "系统盘: "$SYSTEM_DISK
|
|
||||||
case "$SYSTEM_DISK" in
|
|
||||||
/dev/$DISK*)
|
|
||||||
|
|
||||||
fdisksave /dev/$DISK
|
|
||||||
log "此次执行操作功能:$target_function ,目标盘是系统盘:/dev/$DISK"
|
|
||||||
|
|
||||||
PARTITIONSUM=$(get_partition_number $DISK)
|
|
||||||
log "目标盘 $DISK 一共有分区数: $PARTITIONSUM个"
|
|
||||||
if [[ "$PARTITIONSUM" -gt 2 ]];then
|
|
||||||
FREE_SPACE=$(check_free_space $(basename $DISK))
|
|
||||||
log "目标盘 $DISK 有剩余空间: $FREE_SPACE Gb"
|
|
||||||
if [[ "$FREE_SPACE" -gt 2 ]]; then
|
|
||||||
NEXTPART=$(get_next_partition_number $DISK)
|
|
||||||
else
|
|
||||||
NEXTPART=$(get_last_partition_number $DISK)
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
FREE_SPACE=$(check_free_space $(basename $DISK))
|
|
||||||
log "目标盘 $DISK 有剩余空间: $FREE_SPACE Gb"
|
|
||||||
if [[ "$FREE_SPACE" -gt 2 ]]; then
|
|
||||||
NEXTPART=$(get_next_partition_number $DISK)
|
|
||||||
else
|
|
||||||
|
|
||||||
log "目标盘 $SYSTEM_DISK 没有足够的剩余空间!"
|
|
||||||
expquit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
|
|
||||||
log "此次执行操作功能:$target_function ,目标盘(非系统盘)是:/dev/$DISK"
|
|
||||||
PARTITIONSUM=$(get_partition_number $DISK)
|
|
||||||
log "目标盘 $DISK 一共有分区数: $PARTITIONSUM个"
|
|
||||||
if [[ "$PARTITIONSUM" -gt 1 ]];then
|
|
||||||
FREE_SPACE=$(check_free_space $(basename $DISK))
|
|
||||||
log "目标盘 $DISK 有剩余空间: $FREE_SPACE Gb"
|
|
||||||
|
|
||||||
[[ $FREE_SPACE -gt 2 ]] && NEXTPART=$(get_next_partition_number $DISK) || NEXTPART=$(get_last_partition_number $DISK)
|
|
||||||
else
|
|
||||||
NEXTPART=1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
log "定位到操作目标设备分区:/dev/$DISK$NEXTPART"
|
|
||||||
case "$DISK" in
|
|
||||||
vd*) fdiskB $DISK $NEXTPART;;
|
|
||||||
sd*) fdiskB $DISK $NEXTPART;;
|
|
||||||
nvme*) fdiskB $DISK p$NEXTPART;;
|
|
||||||
mmc*) fdiskB $DISK p$NEXTPART;;
|
|
||||||
*)
|
|
||||||
log "目标设备/dev/$DISK暂不支持!请联系作者sirpdboy!"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
expquit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
|
|
||||||
[ -f $LOCK ] && exit
|
|
||||||
[ x$xBOOT = x1 ] || autopart
|
|
||||||
rm -f $LOCK 2>/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
stop () {
|
|
||||||
|
|
||||||
rm -f $LOCK 2>/dev/null
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
boot() {
|
|
||||||
xBOOT=1 start
|
|
||||||
}
|
|
||||||
|
|
||||||
expquit() {
|
|
||||||
rm -f $LOCK
|
|
||||||
uci -q set fstab.@global[0].anon_mount='1'
|
|
||||||
uci -q set fstab.@global[0].auto_mount='1'
|
|
||||||
uci commit fstab
|
|
||||||
[ -e "/etc/config/dockerd" ] && /etc/init.d/dockerd restart >/dev/null 2> /dev/null
|
|
||||||
sleep 2
|
|
||||||
[ $1 = 3 ] && log "重启中...\n" &&reboot
|
|
||||||
exit $1
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
1043
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
1
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
chmod +x /etc/init.d/partexp >/dev/null 2>&1
|
|
||||||
[ `uci -q get partexp.global` ] || uci set partexp.global=global
|
|
||||||
rm -rf /tmp/luci-modulecache /tmp/luci-indexcache*
|
|
||||||
exit 0
|
|
||||||
809
luci-app-partexp/luci-app-partexp/root/usr/bin/partexp
Normal file
809
luci-app-partexp/luci-app-partexp/root/usr/bin/partexp
Normal file
@@ -0,0 +1,809 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright (C) 2021-2025 sirpdboy <herboy2008@gmail.com> https://github.com/sirpdboy/luci-app-partexp
|
||||||
|
#
|
||||||
|
# This is free software, licensed under the Apache License, Version 2.0 .
|
||||||
|
#
|
||||||
|
|
||||||
|
CONFIG="partexp"
|
||||||
|
LOCK=/var/lock/$CONFIG.lock
|
||||||
|
LOGD=/var/$CONFIG
|
||||||
|
LOG=$LOGD/bk$CONFIG.log
|
||||||
|
|
||||||
|
[ -d "$LOGD" ] || mkdir -p $LOGD
|
||||||
|
|
||||||
|
|
||||||
|
init_env() {
|
||||||
|
[ ! -f "$LOG" ] && echo " " > $LOG
|
||||||
|
}
|
||||||
|
|
||||||
|
gen_log() {
|
||||||
|
log " == 开始执行 == " | tee -a $LOG
|
||||||
|
}
|
||||||
|
|
||||||
|
log(){
|
||||||
|
echo -e " $(date +'%Y-%m-%d %H:%M:%S') $*" | tee -a $LOG
|
||||||
|
}
|
||||||
|
|
||||||
|
logn(){
|
||||||
|
echo -e " $*" | tee -a $LOG
|
||||||
|
}
|
||||||
|
|
||||||
|
load_uci_config() {
|
||||||
|
if [ -f "/etc/config/$CONFIG" ]; then
|
||||||
|
target_function=$(uci -q get $CONFIG.global.target_function)
|
||||||
|
target_disk=$(uci -q get $CONFIG.global.target_disk)
|
||||||
|
keep_config=$(uci -q get $CONFIG.global.keep_config)
|
||||||
|
format_type=$(uci -q get $CONFIG.global.format_type)
|
||||||
|
|
||||||
|
target_function="${target_function:-/opt}"
|
||||||
|
keep_config="${keep_config:-0}"
|
||||||
|
format_type="${format_type:-0}"
|
||||||
|
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log "警告:配置文件不存在"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
is_disk_mounted() {
|
||||||
|
DISK=$1
|
||||||
|
if mount | grep -q "$DISK "; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
mount_device() {
|
||||||
|
local DEVICE=$1
|
||||||
|
local MOUNT_POINT=$2
|
||||||
|
local TYPE=$3
|
||||||
|
|
||||||
|
if [ ! -e "$DEVICE" ]; then
|
||||||
|
log "设备 $DEVICE 不存在"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$MOUNT_POINT" ]; then
|
||||||
|
log "挂载点 $MOUNT_POINT 不存在,正在创建..."
|
||||||
|
mkdir -p "$MOUNT_POINT"
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
log "无法创建挂载点 $MOUNT_POINT"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if mount | grep -q "$DEVICE"; then
|
||||||
|
log "设备 $DEVICE 已挂载到其他位置"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if mount | grep -q "$MOUNT_POINT"; then
|
||||||
|
log "挂载点 $MOUNT_POINT 已被其他设备占用"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "正在挂载 $DEVICE 到 $MOUNT_POINT..."
|
||||||
|
|
||||||
|
mount $TYPE "$DEVICE" "$MOUNT_POINT" >/dev/null 2> /dev/null
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
log "挂载成功: $DEVICE -> $MOUNT_POINT"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log "挂载失败: $DEVICE -> $MOUNT_POINT"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 取消硬盘挂载
|
||||||
|
umount_disk() {
|
||||||
|
DISK=$1
|
||||||
|
MOUNT=''
|
||||||
|
eval $(block info "$DISK" | grep -o -e "MOUNT=\S*")
|
||||||
|
if [ "$MOUNT" ]; then
|
||||||
|
umount $DISK 2>/dev/null
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
log "取消挂载:$DISK成功"
|
||||||
|
else
|
||||||
|
log "取消挂载:$DISK失败,请手动操作"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "设备/dev/$DISK未挂载"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# 从 block info 中提取指定字段的值
|
||||||
|
get_block() {
|
||||||
|
local DISK=$1
|
||||||
|
local TYPE=$2
|
||||||
|
local value
|
||||||
|
value=`mount | grep $DISK |awk -F $TYPE '{print $2}' |awk '{print $1}' | head -1`
|
||||||
|
echo $value
|
||||||
|
}
|
||||||
|
|
||||||
|
# 检查是否有共享挂载(如 Samba 或 NFS)
|
||||||
|
check_shared_mount() {
|
||||||
|
DISK=$1
|
||||||
|
if [ -f /etc/config/samba ]; then
|
||||||
|
SHARED=$(grep -q "/dev/$DISK" /etc/config/samba)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
log "检测到 Samba 共享挂载: /dev/$DISK"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f /etc/exports ]; then
|
||||||
|
SHARED=$(grep -q "/dev/$DISK" /etc/exports)
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
log "检测到 NFS 共享挂载: /dev/$DISK"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
usamba(){
|
||||||
|
s=$1
|
||||||
|
[ -e "/etc/config/$s" ] && {
|
||||||
|
msum=$(grep -c "config sambashare" /etc/config/$s)
|
||||||
|
for i in $(seq 0 $((msum)));do
|
||||||
|
pdev=`uci -q get $s.@sambashare[$i].path `
|
||||||
|
[ "$pdev" = "$2" ] && {
|
||||||
|
uci delete $s.@sambashare[$i]
|
||||||
|
uci commit $s
|
||||||
|
log "分区/dev/$b被挂载$MOUNT共享使用,删除$s共享成功!"
|
||||||
|
sleep 5
|
||||||
|
}
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -f /etc/config/$s ]; then
|
||||||
|
sed -i "/\/dev\/$b/d" /etc/config/$s
|
||||||
|
/etc/init.d/$s restart
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -f /etc/exports ]; then
|
||||||
|
sed -i "/\/dev\/$b/d" /etc/exports
|
||||||
|
/etc/init.d/nfs restart
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
is_disk_partitioned() {
|
||||||
|
PARTITION_COUNT=$(fdisk -l /dev/$1 2>/dev/null | grep -E "^/dev/$2" | wc -l)
|
||||||
|
if [[ "$PARTITION_COUNT" -gt 0 ]]; then
|
||||||
|
echo 1
|
||||||
|
else
|
||||||
|
echo 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
partednew(){
|
||||||
|
DISK=$1
|
||||||
|
parted -s /dev/$DISK mklabel gpt
|
||||||
|
parted -s /dev/$DISK mkpart primary ext4 1MiB -1
|
||||||
|
}
|
||||||
|
|
||||||
|
fdisknew(){
|
||||||
|
echo -e "n\np\n\n\n\nw" | fdisk /dev/$1 >/dev/null 2> /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
fdisksave(){
|
||||||
|
echo -e "n\w" | fdisk /dev/$1 >/dev/null 2> /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# 格式化磁盘函数 DISK=/dev/sda1 ;TYPE=btrfs
|
||||||
|
format_disk() {
|
||||||
|
local DISK=$1
|
||||||
|
local TYPE=$2
|
||||||
|
[[ $TYPE == '0' || $TYPE == '' ]] && TYPE="ext4"
|
||||||
|
log "正在格式化 $DISK "
|
||||||
|
mkfs.$TYPE -F "$DISK" >/dev/null 2>/dev/null
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
log "格式化 $TYPE 成功 $DISK"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log "格式化 $TYPE 失败 $DISK"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# 获取系统盘
|
||||||
|
get_system_disk() {
|
||||||
|
rom_dev=$(df -h | grep boot | awk '{print $1}' | head -1 )
|
||||||
|
[ -z ${rom_dev} ] && rom_dev=`df -P /boot 2>/dev/null | awk 'NR==2 {print $1}'`
|
||||||
|
[ -z ${rom_dev} ] && rom_dev=`blkid -t TYPE="squashfs" |awk -F : '{print $1}'`
|
||||||
|
[ -z ${rom_dev} ] && TDISK=`lsblk -l -o NAME,MOUNTPOINTS | grep -E '/rom'| grep -v '/rom/' | head -1 | awk '{print $1}'` && rom_dev="/dev/$TDISK"
|
||||||
|
if [ -n "$rom_dev" ]; then
|
||||||
|
case "$rom_dev" in
|
||||||
|
/dev/sd[a-z][0-9]*)
|
||||||
|
disk=$(echo "$rom_dev" | sed 's/[0-9]*$//')
|
||||||
|
;;
|
||||||
|
/dev/nvme[0-9]*n[0-9]*p[0-9]*)
|
||||||
|
disk=$(echo "$rom_dev" | sed 's/p[0-9]*$//')
|
||||||
|
;;
|
||||||
|
/dev/mmcblk[0-9]*p[0-9]*)
|
||||||
|
disk=$(echo "$rom_dev" | sed 's/p[0-9]*$//')
|
||||||
|
;;
|
||||||
|
/dev/vd[a-z][0-9]*)
|
||||||
|
disk=$(echo "$rom_dev" | sed 's/[0-9]*$//')
|
||||||
|
;;
|
||||||
|
/dev/mtdblock*)
|
||||||
|
disk="$rom_dev"
|
||||||
|
;;
|
||||||
|
/dev/ubiblock*)
|
||||||
|
disk="$rom_dev"
|
||||||
|
;;
|
||||||
|
/dev/root)
|
||||||
|
# 特殊处理 /dev/root
|
||||||
|
disk=$(lsblk -no PKNAME / 2>/dev/null)
|
||||||
|
if [ -n "$disk" ]; then
|
||||||
|
disk="/dev/$disk"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# 尝试通用匹配
|
||||||
|
if echo "$rom_dev" | grep -qE '^/dev/[a-z]+[0-9]+'; then
|
||||||
|
disk=$(echo "$rom_dev" | sed 's/[0-9]*$//')
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$disk" ] && [ ! -b "$disk" ]; then
|
||||||
|
disk=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$disk"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_all_disks() {
|
||||||
|
DISKS=`find /dev -regex '.*/\(sd[a-z]\|mmcblk[0-9]\+\|sata[a-z]\|nvme[0-9]\+n[0-9]\+\|vd[a-z]\)$'`
|
||||||
|
echo "$DISKS"
|
||||||
|
}
|
||||||
|
|
||||||
|
check_part_space() {
|
||||||
|
DISK=$1
|
||||||
|
info=$(lsblk -no SIZE,FSTYPE,MOUNTPOINT "$DISK" | awk '{print $1}')
|
||||||
|
if [ -z "$info" ]; then
|
||||||
|
echo "物理大小: 未知(可能是未格式化的裸分区)"
|
||||||
|
else
|
||||||
|
echo $info |awk -F '.' '{print $1}' | sed 's/[A-Za-z]//g'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
check_free_space() {
|
||||||
|
DISK=$1
|
||||||
|
PARTED_OUTPUT=$(parted -s /dev/$DISK unit GB print free 2>/dev/null)
|
||||||
|
FREE_SPACE=$(echo "$PARTED_OUTPUT" | grep "Free Space" | awk '{print $3}' )
|
||||||
|
echo $FREE_SPACE |awk -F '.' '{print $1}' | sed 's/[A-Za-z]//g'
|
||||||
|
}
|
||||||
|
|
||||||
|
show_partition_info() {
|
||||||
|
local partition="$1"
|
||||||
|
if [ ! -e "$partition" ]; then
|
||||||
|
logn "错误:分区 $partition 不存在!"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
logn " === 分区信息 [$partition] ==="
|
||||||
|
local lsblk_info=$(lsblk -no SIZE,FSTYPE,MOUNTPOINT "$partition" 2>/dev/null)
|
||||||
|
if [ -z "$lsblk_info" ]; then
|
||||||
|
logn "物理大小: 未知(可能是未格式化的裸分区)"
|
||||||
|
else
|
||||||
|
local size=$(echo "$lsblk_info" | awk '{print $1}')
|
||||||
|
local fstype=$(echo "$lsblk_info" | awk '{print $2}')
|
||||||
|
local mountpoint=$(echo "$lsblk_info" | awk '{print $3}')
|
||||||
|
logn "物理大小: $size 文件系统: ${fstype:-未知} 挂载点: ${mountpoint:-未挂载}"
|
||||||
|
fi
|
||||||
|
if df "$partition" &>/dev/null; then
|
||||||
|
local df_info=$(df -h "$partition" | tail -n 1 | awk 'NR=2 {print $2,$3,$4,$5}')
|
||||||
|
logn "[已挂载] 空间使用情况:"
|
||||||
|
logn "总容量: $(echo "$df_info" | awk '{print $1}') 已用: $(echo "$df_info" | awk '{print $2}') 剩余: $(echo "$df_info" | awk '{print $3}') 使用率: $(echo "$df_info" | awk '{print $4}')"
|
||||||
|
|
||||||
|
else
|
||||||
|
logn "[未挂载] 无法查询使用情况(需先挂载)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local disk="${partition%[0-9]*}"
|
||||||
|
local part_num="${partition##*[!0-9]}"
|
||||||
|
logn "分区表信息:"
|
||||||
|
logn `parted -s "$disk" unit MiB print | grep -w "^ $part_num" | awk '{print "起始: " $2 " MiB | 结束: " $3 " MiB | 类型: " $5}'`
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
get_next_partition_number() {
|
||||||
|
DISK=$1
|
||||||
|
PARTITIONS=$(fdisk -l /dev/$DISK 2>/dev/null | grep -v boot | grep -E "^/dev/$DISK" | awk '{print $1}' | sed 's/\/dev\/[a-z]*//g' | awk -F '[^0-9]+' '{print $NF}')
|
||||||
|
MAX_PARTITION=$(echo "$PARTITIONS" | sort -n | tail -n 1)
|
||||||
|
NEXT_PARTITION=$(awk -v n="$MAX_PARTITION" 'BEGIN { print n + 1 }')
|
||||||
|
echo "$NEXT_PARTITION"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_last_partition_number() {
|
||||||
|
DISK=$1
|
||||||
|
PARTITIONS=$(fdisk -l /dev/$DISK 2>/dev/null | grep -v boot | grep -E "^/dev/$DISK" | awk '{print $1}' | sed 's/\/dev\/[a-z]*//g' | awk -F '[^0-9]+' '{print $NF}')
|
||||||
|
MAX_PARTITION=$(echo "$PARTITIONS" | sort -n | tail -n 1)
|
||||||
|
echo "$MAX_PARTITION"
|
||||||
|
}
|
||||||
|
|
||||||
|
get_partition_number() {
|
||||||
|
DISK=$1
|
||||||
|
PARTITIONS=$(fdisk -l /dev/$DISK 2>/dev/null | grep -v boot | grep -E "^/dev/$DISK" | awk '{print $1}' | sed 's/\/dev\/[a-z]*//g' | wc -l)
|
||||||
|
echo "$PARTITIONS"
|
||||||
|
}
|
||||||
|
|
||||||
|
rootpt_resize() {
|
||||||
|
if [ ! -e /etc/rootpt-resize ]; then
|
||||||
|
log "--->请稍侯,系统根分区扩展中<---"
|
||||||
|
ROOTBLK="$(readlink -f /sys/dev/block/"$(awk -e '$9="/dev/root"{print $3}' /proc/self/mountinfo)")"
|
||||||
|
ROOTDISK="/dev/$(basename "${ROOTBLK%/}")"
|
||||||
|
ROOTPART="${ROOTBLK##*[^0-9]}"
|
||||||
|
partplace=$(fdisk -l 2>/dev/null | grep "$ROOTDISK" | awk '{print $5}' )
|
||||||
|
log "--->根分区$ROOTDISK:$partplace <---"
|
||||||
|
sleep 3
|
||||||
|
parted -f -s "${ROOTDISK}" resizepart "${ROOTPART}" 100%
|
||||||
|
mount_root done
|
||||||
|
touch /etc/rootpt-resize
|
||||||
|
sleep 3
|
||||||
|
log "--->系统根分区扩展成功!<---"
|
||||||
|
partplace=$(fdisk -l 2>/dev/null | grep "$ROOTDISK" | awk '{print $5}' )
|
||||||
|
log "--->根分区$ROOTDISK扩展后容量:$partplace <---"
|
||||||
|
log "--->如果没生效,请重启设备<---"
|
||||||
|
expquit 2
|
||||||
|
else
|
||||||
|
log "已经扩展过或者挂载分区过,请删除分区或者重置重新操作或者联系作者sirpdboy!"
|
||||||
|
expquit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
rootfs_resize() {
|
||||||
|
if [ ! -e /etc/rootfs-resize ] && [ -e /etc/rootpt-resize ]; then
|
||||||
|
log "--->请稍侯,系统根分区扩展中<---"
|
||||||
|
ROOTBLK="$(readlink -f /sys/dev/block/"$(awk -e '$9="/dev/root"{print $3}' /proc/self/mountinfo)")"
|
||||||
|
ROOTPART="${ROOTBLK##*[^0-9]}"
|
||||||
|
ROOTDISK="/dev/$(basename "${ROOTBLK%/}")"
|
||||||
|
partplace=$(fdisk -l 2>/dev/null | grep "$ROOTDISK" | awk '{print $5}' )
|
||||||
|
log "--->根分区$ROOTDISK:$partplace <---"
|
||||||
|
sleep 3
|
||||||
|
parted -f -s "${ROOTDISK}" resizepart "${ROOTPART}" 100%
|
||||||
|
mount_root done
|
||||||
|
touch /etc/rootpt-resize
|
||||||
|
sleep 3
|
||||||
|
log "--->系统根分区扩展成功!<---"
|
||||||
|
partplace=$(fdisk -l 2>/dev/null | grep "$ROOTDISK" | awk '{print $5}' )
|
||||||
|
log "--->根分区$ROOTDISK扩展后容量:$partplace <---"
|
||||||
|
log "--->请稍侯,系统overlay扩展中<---"
|
||||||
|
df -h /overlay | awk 'NR=2 {printf " overlay扩展前: 总容量: %s 已用: %s 剩余: %s 使用率: %s", $2, $3, $4, $5}'
|
||||||
|
LOOPDEV="$(awk -e '$5="/overlay"{print $9}' /proc/self/mountinfo)"
|
||||||
|
if [ -z "${LOOPDEV}" ]; then
|
||||||
|
LOOP_DEV="$(losetup -f)"
|
||||||
|
losetup "${LOOPDEV}" "${ROOTDEV}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
FSTYPE=$(blkid -o value -s TYPE "$LOOPDEV" 2>/dev/null)
|
||||||
|
umount -l /overlay
|
||||||
|
mount -t tmpfs -o size=128M tmpfs /overlay
|
||||||
|
losetup -d /dev/loop0
|
||||||
|
losetup -fP ${ROOTDISK}
|
||||||
|
|
||||||
|
case "$FSTYPE" in
|
||||||
|
f2fs)
|
||||||
|
umount /overlay || { log "错误:无法卸载 /overlay"; expquit 1; }
|
||||||
|
fsck.f2fs -f "$LOOPDEV"
|
||||||
|
resize.f2fs -f "$LOOPDEV" || { log "错误:f2fs 调整大小失败"; expquit 1; }
|
||||||
|
;;
|
||||||
|
ext4)
|
||||||
|
resize2fs -f "$LOOPDEV" || { log "错误:ext4 调整大小失败"; expquit 1; }
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log "--->分区格式 $FSTYPE 不识别,overlay 扩展失败!<---"
|
||||||
|
expquit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
mount_root done
|
||||||
|
touch /etc/rootfs-resize
|
||||||
|
sleep 3
|
||||||
|
log "--->系统overlay扩展成功!<---"
|
||||||
|
df -h /overlay | awk 'NR=2 {printf " overlay扩展后: 总容量: %s 已用: %s 剩余: %s 使用率: %s", $2, $3, $4, $5}'
|
||||||
|
log "--->如果没生效,请重启设备<---"
|
||||||
|
expquit 2
|
||||||
|
else
|
||||||
|
log "已经扩展过或者挂载分区过,请删除分区或者重置重新操作或者联系作者sirpdboy!"
|
||||||
|
expquit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
get_config() {
|
||||||
|
config_get target_function $1 target_function 1
|
||||||
|
config_get target_disk $1 target_disk 1
|
||||||
|
config_get_bool keep_config $1 keep_config 1
|
||||||
|
config_get format_type $1 format_type
|
||||||
|
}
|
||||||
|
|
||||||
|
# 修改 fdiskB 函数,使用环境变量参数
|
||||||
|
fdiskB() {
|
||||||
|
a=$1
|
||||||
|
b=$1$2
|
||||||
|
log "开始检测目标$a信息"
|
||||||
|
log "检测/dev/$a是否需要分区和格式化"
|
||||||
|
|
||||||
|
block detect > /etc/config/fstab
|
||||||
|
uci -q set fstab.@global[0].anon_mount='0'
|
||||||
|
uci -q set fstab.@global[0].auto_mount='0'
|
||||||
|
uci commit fstab
|
||||||
|
|
||||||
|
if [ "$target_function" = '/opt' ]; then
|
||||||
|
/etc/init.d/dockerd stop >/dev/null 2> /dev/null
|
||||||
|
amount=`mount |grep /opt | awk '{print $1}'`
|
||||||
|
if [ -n "$amount" ]; then
|
||||||
|
umount $amount >/dev/null 2> /dev/null
|
||||||
|
log "取消/opt之前的挂载$amount成功!"
|
||||||
|
fi
|
||||||
|
for OPT in $(mount |grep /opt | awk '{print $3}');do
|
||||||
|
umount $OPT >/dev/null 2> /dev/null
|
||||||
|
log "取消/opt之前的挂载$OPT成功!"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -d "/mnt/$b" ] || mkdir -p /mnt/$b
|
||||||
|
|
||||||
|
if is_disk_mounted "/dev/$b"; then
|
||||||
|
log "设备 /dev/$b 已挂载,尝试取消挂载..."
|
||||||
|
if check_shared_mount $b; then
|
||||||
|
usamba samba4 $MOUNT
|
||||||
|
usamba samba $MOUNT
|
||||||
|
sleep 5
|
||||||
|
fi
|
||||||
|
umount_disk "/dev/$b"
|
||||||
|
[ $? -ne 0 ] || umount_disk "/mnt/$b"
|
||||||
|
else
|
||||||
|
log "设备/dev/$b未挂载"
|
||||||
|
isfdisk=0
|
||||||
|
isP=$(is_disk_partitioned $a $b)
|
||||||
|
if [ "$isP" = '0' ]; then
|
||||||
|
fdisksave $a
|
||||||
|
fdisknew $a
|
||||||
|
sleep 2
|
||||||
|
isfdisk=1
|
||||||
|
fi
|
||||||
|
isP=$(is_disk_partitioned $a $b)
|
||||||
|
if [[ "$isP" = '1' && "$isfdisk" = 1 ]]; then
|
||||||
|
log "分区$b建立成功!"
|
||||||
|
elif [[ "$isP" = '1' && "$isfdisk" = 0 ]]; then
|
||||||
|
log "检测目标分区$b已存在."
|
||||||
|
else
|
||||||
|
log "分区$b建立失败,请检查$b硬盘空间!"
|
||||||
|
expquit 1
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if is_disk_mounted "/dev/$b"; then
|
||||||
|
umount /dev/$b >/dev/null 2> /dev/null
|
||||||
|
[ $? -ne 0 ] && block umount /dev/$b >/dev/null 2> /dev/null
|
||||||
|
fi
|
||||||
|
sleep 5
|
||||||
|
if [[ "$target_function" = "/" || "$target_function" = "/overlay" ]]; then
|
||||||
|
format_disk "/dev/$b" $format_type
|
||||||
|
elif [[ "$format_type" != "0" || "$isfdisk" = "1" ]]; then
|
||||||
|
format_disk "/dev/$b" $format_type
|
||||||
|
else
|
||||||
|
log "设备/dev/$b如果未格式化,可能无法正常使用."
|
||||||
|
fi
|
||||||
|
|
||||||
|
TYPE=''
|
||||||
|
eval $(blkid "/dev/$b" | grep -o -e "TYPE=\S*")
|
||||||
|
log "检测设备/dev/$b分区$TYPE格式!"
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
if [ "$TYPE" = "ntfs" ]; then
|
||||||
|
if [ `which ntfs-3g` ]; then
|
||||||
|
if is_disk_mounted "/mnt/$b"; then
|
||||||
|
mount_device /dev/$b /mnt/$b "-t ntfs-3g"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if is_disk_mounted "/mnt/$b"; then
|
||||||
|
mount_device /dev/$b /mnt/$b "-t ntfs3"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
mount /dev/$b /mnt/$b >/dev/null 2> /dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
UUID=''
|
||||||
|
eval $(block info /dev/$b | grep -o -e "UUID=\S*")
|
||||||
|
if [ ! "$UUID" ]; then
|
||||||
|
log "获取/dev/$b设备UUID信息失败!"
|
||||||
|
expquit 1
|
||||||
|
else
|
||||||
|
log "获取/dev/$b设备UUID信息成功!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sleep 1
|
||||||
|
case "$target_function" in
|
||||||
|
"/overlay")
|
||||||
|
if [ "$keep_config" = "1" ]; then
|
||||||
|
tar -C /overlay -cvf - . | tar -C /mnt/$b/ -xf - || tar -C /rom/overlay -cvf - . | tar -C /mnt/$b/ -xf -
|
||||||
|
umount /dev/$b >/dev/null 2> /dev/null
|
||||||
|
[ $? -ne 0 ] && umount /mnt/$b >/dev/null 2> /dev/null
|
||||||
|
[ $? -ne 0 ] && block umount /dev/$b >/dev/null 2> /dev/null
|
||||||
|
block detect > /etc/config/fstab
|
||||||
|
OVERLAY=`uci -q get fstab.@mount[0].target`
|
||||||
|
if [[ "$OVERLAY" = "/overlay" || "$OVERLAY" = "/dev/loop0" ]]; then
|
||||||
|
uci -q set fstab.@mount[0].uuid="${UUID}"
|
||||||
|
uci -q set fstab.@mount[0].target='/overlay'
|
||||||
|
uci -q set fstab.@mount[0].enabled='0'
|
||||||
|
fi
|
||||||
|
msum=$(grep -c "'mount'" /etc/config/fstab)
|
||||||
|
for i in $(seq 0 $((msum-1)))
|
||||||
|
do
|
||||||
|
zuuid=`uci -q get fstab.@mount[$i].uuid`
|
||||||
|
[ $? -ne 0 ] && break
|
||||||
|
if [ "$zuuid" = "$UUID" ]; then
|
||||||
|
uci -q set fstab.@mount[$i].target="/overlay"
|
||||||
|
uci -q set fstab.@mount[$i].enabled='1'
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
uci set fstab.@global[0].delay_root="15"
|
||||||
|
uci commit fstab
|
||||||
|
log "保留数据overlay扩展/dev/$b成功!"
|
||||||
|
eval $(block info /dev/$b | grep -o -e "MOUNT=\S*")
|
||||||
|
echo $MOUNT $a> /etc/partexppath
|
||||||
|
log "扩容成功!可直接【在线升级】扩容升级了!"
|
||||||
|
show_partition_info /dev/$b
|
||||||
|
sleep 3
|
||||||
|
|
||||||
|
expquit 2
|
||||||
|
else
|
||||||
|
umount /dev/$b >/dev/null 2> /dev/null
|
||||||
|
[ $? -ne 0 ] && umount /mnt/$b >/dev/null 2> /dev/null
|
||||||
|
[ $? -ne 0 ] && block umount /dev/$b >/dev/null 2> /dev/null
|
||||||
|
block detect > /etc/config/fstab
|
||||||
|
OVERLAY=`uci -q get fstab.@mount[0].target`
|
||||||
|
if [[ "$OVERLAY" = "/overlay" || "$OVERLAY" = "/dev/loop0" ]]; then
|
||||||
|
uci -q set fstab.@mount[0].uuid="${UUID}"
|
||||||
|
uci -q set fstab.@mount[0].target='/overlay'
|
||||||
|
uci -q set fstab.@mount[0].enabled='0'
|
||||||
|
fi
|
||||||
|
msum=$(grep -c "'mount'" /etc/config/fstab)
|
||||||
|
for i in $(seq 0 $((msum-1)))
|
||||||
|
do
|
||||||
|
zuuid=`uci -q get fstab.@mount[$i].uuid`
|
||||||
|
[ $? -ne 0 ] && break
|
||||||
|
if [ "$zuuid" = "$UUID" ]; then
|
||||||
|
uci -q set fstab.@mount[$i].target="/overlay"
|
||||||
|
uci -q set fstab.@mount[$i].enabled='1'
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
uci set fstab.@global[0].delay_root="15"
|
||||||
|
uci commit fstab
|
||||||
|
log "不保留数据overlay扩展/dev/$b成功!"
|
||||||
|
eval $(block info /dev/$b | grep -o -e "MOUNT=\S*")
|
||||||
|
echo $MOUNT $a> /etc/partexppath
|
||||||
|
log "扩容成功!可直接【在线升级】扩容升级了!"
|
||||||
|
show_partition_info /dev/$b
|
||||||
|
sleep 3
|
||||||
|
expquit 2
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"/opt")
|
||||||
|
umount /dev/$b >/dev/null 2> /dev/null
|
||||||
|
[ $? -ne 0 ] && umount /mnt/$b >/dev/null 2> /dev/null
|
||||||
|
[ $? -ne 0 ] && block umount /dev/$b >/dev/null 2> /dev/null
|
||||||
|
block detect > /etc/config/fstab
|
||||||
|
mkdir -p $target_function
|
||||||
|
msum=$(grep -c "'mount'" /etc/config/fstab)
|
||||||
|
mount_device /dev/$b "$target_function"
|
||||||
|
for i in $(seq 0 $((msum-1)))
|
||||||
|
do
|
||||||
|
zuuid=`uci -q get fstab.@mount[$i].uuid`
|
||||||
|
[ $? -ne 0 ] && break
|
||||||
|
if [ "$zuuid" = "$UUID" ]; then
|
||||||
|
uci -q set fstab.@mount[$i].target="$target_function"
|
||||||
|
uci -q set fstab.@mount[$i].enabled='1'
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
uci commit fstab
|
||||||
|
if is_disk_mounted "/opt"; then
|
||||||
|
log "/dev/$b分区扩容和挂载到$target_function成功!"
|
||||||
|
eval $(block info /dev/$b | grep -o -e "MOUNT=\S*")
|
||||||
|
echo $MOUNT $a> /etc/partexppath
|
||||||
|
log "扩容成功!可直接【在线升级】扩容升级了!"
|
||||||
|
show_partition_info /dev/$b
|
||||||
|
expquit 2
|
||||||
|
else
|
||||||
|
log "/dev/$b分区扩容和挂载到$target_function失败!"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
"/")
|
||||||
|
ROOTBLK="$(readlink -f /sys/dev/block/"$(awk '$9="/dev/root"{print $3}' /proc/self/mountinfo)")"
|
||||||
|
[ -z "$ROOTBLK" ] && { log "错误:无法获取根分区块设备"; expquit 1; }
|
||||||
|
ROOTDISK="/dev/$(basename "${ROOTBLK%/}")"
|
||||||
|
FSTYPE=$(blkid -o value -s TYPE "$ROOTDISK" 2>/dev/null)
|
||||||
|
if [[ "$FSTYPE" != "squashfs" && -n "$FSTYPE" ]]; then
|
||||||
|
if [ "$target_function" = '/' ]; then
|
||||||
|
FREE_SPACE=$(check_free_space $(basename $DISK))
|
||||||
|
log "目标盘 $ROOT_PART $FSTYPE有剩余空间: $FREE_SPACE Gb"
|
||||||
|
if [[ "$FREE_SPACE" -gt 2 ]]; then
|
||||||
|
rootpt_resize
|
||||||
|
expquit 2
|
||||||
|
else
|
||||||
|
log "目标盘 $SYSTEM_DISK $FSTYPE没有足够的剩余空间!"
|
||||||
|
expquit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "$target_function" = '/overlay' ]; then
|
||||||
|
FREE_SPACE=$(check_free_space $(basename $DISK))
|
||||||
|
log "目标盘 $ROOT_PART $FSTYPE有剩余空间: $FREE_SPACE Gb"
|
||||||
|
if [[ "$FREE_SPACE" -gt 2 ]]; then
|
||||||
|
rootfs_resize
|
||||||
|
eval $(block info /dev/$b | grep -o -e "MOUNT=\S*")
|
||||||
|
log "/dev/$b分区扩容和挂载到$target_function成功!"
|
||||||
|
echo $MOUNT $a> /etc/partexppath
|
||||||
|
log "扩容成功!可直接【在线升级】扩容升级了!"
|
||||||
|
show_partition_info /dev/$b
|
||||||
|
expquit 2
|
||||||
|
else
|
||||||
|
log "目标盘 $SYSTEM_DISK $FSTYPE没有足够的剩余空间!"
|
||||||
|
expquit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log "目标硬盘不支持/根分区扩展!请换EXT4固件!"
|
||||||
|
fi
|
||||||
|
sleep 3
|
||||||
|
expquit 2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
umount /dev/$b >/dev/null 2> /dev/null
|
||||||
|
[ $? -ne 0 ] && umount /mnt/$b >/dev/null 2> /dev/null
|
||||||
|
[ $? -ne 0 ] && block umount /dev/$b >/dev/null 2> /dev/null
|
||||||
|
block detect > /etc/config/fstab
|
||||||
|
mkdir -p /mnt/$b
|
||||||
|
msum=$(grep -c "'mount'" /etc/config/fstab)
|
||||||
|
mount_device /dev/$b /mnt/$b
|
||||||
|
for i in $(seq 0 $((msum-1)))
|
||||||
|
do
|
||||||
|
zuuid=`uci -q get fstab.@mount[$i].uuid`
|
||||||
|
[ $? -ne 0 ] && break
|
||||||
|
if [ "$zuuid" = "$UUID" ]; then
|
||||||
|
uci -q set fstab.@mount[$i].target="/mnt/$b"
|
||||||
|
uci -q set fstab.@mount[$i].enabled='1'
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
uci commit fstab
|
||||||
|
if is_disk_mounted /mnt/$b; then
|
||||||
|
log "/dev/$b分区扩容和挂载到/mnt/$b成功!"
|
||||||
|
eval $(block info /dev/$b | grep -o -e "MOUNT=\S*")
|
||||||
|
echo $MOUNT $a> /etc/partexppath
|
||||||
|
log "扩容成功!可直接【在线升级】扩容升级了!"
|
||||||
|
show_partition_info /dev/$b
|
||||||
|
expquit 2
|
||||||
|
else
|
||||||
|
log "/dev/$b分区扩容和挂载到/mnt/$b失败!"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
autopart() {
|
||||||
|
|
||||||
|
[ -f $LOCK ] && expquit 1
|
||||||
|
if [ -f "/etc/config/$CONFIG" ]; then
|
||||||
|
target_function=$(uci -q get partexp.global.target_function)
|
||||||
|
target_disk=$(uci -q get partexp.global.target_disk)
|
||||||
|
keep_config=$(uci -q get partexp.global.keep_config)
|
||||||
|
format_type=$(uci -q get partexp.global.format_type)
|
||||||
|
export target_function="${target_function:-/opt}"
|
||||||
|
export keep_config="${keep_config:-0}"
|
||||||
|
export format_type="${format_type:-0}"
|
||||||
|
export target_disk="${target_disk:-sda}"
|
||||||
|
fi
|
||||||
|
touch $LOCK
|
||||||
|
init_env
|
||||||
|
gen_log
|
||||||
|
|
||||||
|
uci -q set fstab.@global[0].anon_mount='0'
|
||||||
|
uci -q set fstab.@global[0].auto_mount='0'
|
||||||
|
uci commit fstab
|
||||||
|
|
||||||
|
[ -e "/etc/config/dockerd" ] && /etc/init.d/dockerd stop >/dev/null 2> /dev/null
|
||||||
|
|
||||||
|
DISK=${target_disk}
|
||||||
|
NEXTPART=1
|
||||||
|
DISKSALL=$(get_all_disks)
|
||||||
|
DISK_COUNT=$(echo "$DISKSALL" | wc -l)
|
||||||
|
log "系统中检测到的硬盘数量: $DISK_COUNT"
|
||||||
|
log "硬盘信息列表:" $DISKSALL
|
||||||
|
|
||||||
|
SYSTEM_DISK=$(get_system_disk)
|
||||||
|
log "系统盘: "$SYSTEM_DISK
|
||||||
|
|
||||||
|
case "$SYSTEM_DISK" in
|
||||||
|
/dev/$DISK*)
|
||||||
|
fdisksave /dev/$DISK
|
||||||
|
log "操作功能:$target_function ,系统盘:/dev/$DISK"
|
||||||
|
|
||||||
|
PARTITIONSUM=$(get_partition_number $DISK)
|
||||||
|
log "目标盘 $DISK 一共有分区数: $PARTITIONSUM个"
|
||||||
|
|
||||||
|
if [[ "$PARTITIONSUM" -gt 2 ]]; then
|
||||||
|
FREE_SPACE=$(check_free_space $(basename $DISK))
|
||||||
|
log "目标盘 $DISK 有剩余空间: $FREE_SPACE Gb"
|
||||||
|
if [[ "$FREE_SPACE" -gt 2 ]]; then
|
||||||
|
NEXTPART=$(get_next_partition_number $DISK)
|
||||||
|
else
|
||||||
|
NEXTPART=$(get_last_partition_number $DISK)
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
FREE_SPACE=$(check_free_space $(basename $DISK))
|
||||||
|
log "目标盘 $DISK 有剩余空间: $FREE_SPACE Gb"
|
||||||
|
if [[ "$FREE_SPACE" -gt 2 ]]; then
|
||||||
|
NEXTPART=$(get_next_partition_number $DISK)
|
||||||
|
else
|
||||||
|
log "目标盘 $SYSTEM_DISK 没有足够的剩余空间!"
|
||||||
|
expquit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
log "操作功能:$target_function ,非系统盘:/dev/$DISK"
|
||||||
|
PARTITIONSUM=$(get_partition_number $DISK)
|
||||||
|
log "目标盘 $DISK 一共有分区数: $PARTITIONSUM个"
|
||||||
|
|
||||||
|
if [[ "$PARTITIONSUM" -gt 1 ]]; then
|
||||||
|
FREE_SPACE=$(check_free_space $(basename $DISK))
|
||||||
|
log "目标盘 $DISK 有剩余空间: $FREE_SPACE Gb"
|
||||||
|
[[ $FREE_SPACE -gt 2 ]] && NEXTPART=$(get_next_partition_number $DISK) || NEXTPART=$(get_last_partition_number $DISK)
|
||||||
|
else
|
||||||
|
NEXTPART=1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
log "定位到操作目标设备分区:/dev/$DISK$NEXTPART"
|
||||||
|
|
||||||
|
case "$DISK" in
|
||||||
|
vd*|sd*) fdiskB $DISK $NEXTPART;;
|
||||||
|
nvme*|mmc*) fdiskB $DISK p$NEXTPART;;
|
||||||
|
# mtdblock*|ubiblock*) fdiskB $DISK $NEXTPART;;
|
||||||
|
*)
|
||||||
|
log "目标设备/dev/$DISK暂不支持!请联系作者sirpdboy!"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
expquit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
rm -f $LOCK 2>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
expquit() {
|
||||||
|
rm -f $LOCK
|
||||||
|
uci -q set fstab.@global[0].anon_mount='1'
|
||||||
|
uci -q set fstab.@global[0].auto_mount='1'
|
||||||
|
uci commit fstab
|
||||||
|
[ -e "/etc/config/dockerd" ] && /etc/init.d/dockerd restart >/dev/null 2> /dev/null
|
||||||
|
[ "$1" = "2" ] && log "如果没生效,请重启设备"
|
||||||
|
sleep 1
|
||||||
|
log "== 操作完成 =="
|
||||||
|
[ "$1" = "3" ] && log "重启中...\n" && reboot
|
||||||
|
exit $1
|
||||||
|
}
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
autopart)
|
||||||
|
"$1"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 {autopart}"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
228
luci-app-partexp/luci-app-partexp/root/usr/libexec/rpcd/partexp
Normal file
228
luci-app-partexp/luci-app-partexp/root/usr/libexec/rpcd/partexp
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
. /usr/share/libubox/jshn.sh
|
||||||
|
|
||||||
|
# 分区扩展服务
|
||||||
|
case "$1" in
|
||||||
|
list)
|
||||||
|
echo '{
|
||||||
|
"autopart": {
|
||||||
|
"description": "Execute automatic partition expansion"
|
||||||
|
},
|
||||||
|
"get_log": {
|
||||||
|
"description": "Get operation log",
|
||||||
|
"arguments": {
|
||||||
|
"position": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"get_devices": {
|
||||||
|
"description": "Get available disk devices"
|
||||||
|
},
|
||||||
|
"get_status": {
|
||||||
|
"description": "Get operation status"
|
||||||
|
},
|
||||||
|
"save_config": {
|
||||||
|
"description": "Save configuration",
|
||||||
|
"arguments": {
|
||||||
|
"target_function": "string",
|
||||||
|
"target_disk": "string",
|
||||||
|
"keep_config": "string",
|
||||||
|
"format_type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
;;
|
||||||
|
call)
|
||||||
|
case "$2" in
|
||||||
|
autopart)
|
||||||
|
if [ -f "/var/run/partexp.lock" ]; then
|
||||||
|
echo '{"error": "Another operation is in progress"}'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查配置文件是否存在
|
||||||
|
if [ ! -f "/etc/config/partexp" ]; then
|
||||||
|
echo '{"error": "Configuration file not found. Please save settings first."}'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 创建锁文件
|
||||||
|
touch /var/run/partexp.lock
|
||||||
|
|
||||||
|
# 执行分区操作(后台异步执行)
|
||||||
|
{
|
||||||
|
# 清空日志文件
|
||||||
|
echo "" > /tmp/partexp.log
|
||||||
|
|
||||||
|
# 调用原 partexp 脚本
|
||||||
|
/usr/bin/partexp autopart > /tmp/partexp.log 2>&1
|
||||||
|
|
||||||
|
# 清理锁文件
|
||||||
|
rm -f /var/run/partexp.lock
|
||||||
|
} &
|
||||||
|
|
||||||
|
echo '{"success": true, "pid": "'$!'", "message": "Partition expansion started"}'
|
||||||
|
;;
|
||||||
|
|
||||||
|
get_log)
|
||||||
|
# 获取操作日志
|
||||||
|
read input
|
||||||
|
json_load "$input"
|
||||||
|
json_get_vars position
|
||||||
|
|
||||||
|
if [ ! -f "/tmp/partexp.log" ]; then
|
||||||
|
echo '{"log": "", "complete": true}'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$position" ] || [ "$position" = "0" ]; then
|
||||||
|
# 从头读取
|
||||||
|
log_content=$(cat /tmp/partexp.log 2>/dev/null | tail -c 2048000)
|
||||||
|
new_position=$(stat -c%s /tmp/partexp.log 2>/dev/null || echo "0")
|
||||||
|
|
||||||
|
json_init
|
||||||
|
json_add_string "log" "$log_content"
|
||||||
|
json_add_boolean "complete" false
|
||||||
|
json_add_string "position" "$new_position"
|
||||||
|
json_dump
|
||||||
|
else
|
||||||
|
# 从指定位置读取
|
||||||
|
position=${position:-0}
|
||||||
|
file_size=$(stat -c%s /tmp/partexp.log 2>/dev/null || echo 0)
|
||||||
|
|
||||||
|
if [ "$position" -lt "$file_size" ]; then
|
||||||
|
log_content=$(tail -c +$((position + 1)) /tmp/partexp.log | head -c 2048000)
|
||||||
|
new_position=$((position + ${#log_content}))
|
||||||
|
|
||||||
|
json_init
|
||||||
|
json_add_string "log" "$log_content"
|
||||||
|
json_add_boolean "complete" false
|
||||||
|
json_add_string "position" "$new_position"
|
||||||
|
json_dump
|
||||||
|
else
|
||||||
|
json_init
|
||||||
|
json_add_string "log" ""
|
||||||
|
json_add_boolean "complete" true
|
||||||
|
json_add_string "position" "$position"
|
||||||
|
json_dump
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
get_devices)
|
||||||
|
# 获取可用设备列表 - 仅磁盘
|
||||||
|
json_init
|
||||||
|
json_add_array "devices"
|
||||||
|
|
||||||
|
# 所有可能的磁盘设备模式
|
||||||
|
disk_patterns="
|
||||||
|
^sd[a-z]$
|
||||||
|
^mmcblk[0-9]+$
|
||||||
|
^nvme[0-9]+n[0-9]+$
|
||||||
|
^vd[a-z]$
|
||||||
|
^hd[a-z]$
|
||||||
|
^xvd[a-z]$
|
||||||
|
^ubd[a-z]+$
|
||||||
|
^dasd[a-z]+$
|
||||||
|
^cciss[0-9]+$
|
||||||
|
^ida[0-9]+$
|
||||||
|
^rd[0-9]+$
|
||||||
|
^mtdblock[0-9]+$
|
||||||
|
^nbd[0-9]+$
|
||||||
|
^zram[0-9]+$
|
||||||
|
"
|
||||||
|
|
||||||
|
for dev in /sys/class/block/*; do
|
||||||
|
dev_name=$(basename "$dev")
|
||||||
|
|
||||||
|
# 跳过分区、loop、dm 设备
|
||||||
|
if [ -f "$dev/partition" ] || [ -d "$dev/loop" ] || [ -d "$dev/dm" ]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 检查是否为磁盘设备
|
||||||
|
is_disk=0
|
||||||
|
for pattern in $disk_patterns; do
|
||||||
|
if echo "$dev_name" | grep -qE "$pattern"; then
|
||||||
|
is_disk=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $is_disk -eq 1 ]; then
|
||||||
|
size="0"
|
||||||
|
if [ -f "$dev/size" ]; then
|
||||||
|
size_sectors=$(cat "$dev/size")
|
||||||
|
size=$((size_sectors / 2048))
|
||||||
|
fi
|
||||||
|
|
||||||
|
json_add_object
|
||||||
|
json_add_string "name" "$dev_name"
|
||||||
|
json_add_string "dev" "/dev/$dev_name"
|
||||||
|
json_add_int "size" "$size"
|
||||||
|
json_close_object
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
json_close_array
|
||||||
|
json_dump
|
||||||
|
;;
|
||||||
|
|
||||||
|
|
||||||
|
get_status)
|
||||||
|
# 获取操作状态
|
||||||
|
if [ -f "/var/run/partexp.lock" ]; then
|
||||||
|
echo '{"running": true}'
|
||||||
|
else
|
||||||
|
echo '{"running": false}'
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
|
||||||
|
save_config)
|
||||||
|
# 保存配置
|
||||||
|
read input
|
||||||
|
json_load "$input"
|
||||||
|
json_get_vars target_function target_disk keep_config format_type
|
||||||
|
|
||||||
|
# 验证参数
|
||||||
|
if [ -z "$target_function" ]; then
|
||||||
|
echo '{"error": "Missing target_function parameter"}'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 设置默认值
|
||||||
|
target_disk="${target_disk:-}"
|
||||||
|
keep_config="${keep_config:-0}"
|
||||||
|
format_type="${format_type:-0}"
|
||||||
|
|
||||||
|
# 构建配置内容
|
||||||
|
CONFIG_FILE="/etc/config/partexp"
|
||||||
|
mkdir -p "$(dirname "$CONFIG_FILE")"
|
||||||
|
|
||||||
|
cat > "$CONFIG_FILE" << EOF
|
||||||
|
# Auto-generated by partexp
|
||||||
|
|
||||||
|
config global global
|
||||||
|
option target_function '$target_function'
|
||||||
|
option target_disk '$target_disk'
|
||||||
|
option keep_config '$keep_config'
|
||||||
|
option format_type '$format_type'
|
||||||
|
EOF
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo '{"success": true, "message": "Configuration saved"}'
|
||||||
|
else
|
||||||
|
echo '{"error": "Failed to save configuration"}'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo '{"error": "Method not found"}'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo '{"error": "Invalid action"}'
|
||||||
|
;;
|
||||||
|
esac
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"admin/system/partexp": {
|
||||||
|
"title": "Partition Expansion",
|
||||||
|
"order": 54,
|
||||||
|
"action": {
|
||||||
|
"type": "view",
|
||||||
|
"path": "partexp"
|
||||||
|
},
|
||||||
|
"acl": ["luci-app-partexp"],
|
||||||
|
"depends": {
|
||||||
|
"acl": ["luci-app-partexp"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,22 +2,18 @@
|
|||||||
"luci-app-partexp": {
|
"luci-app-partexp": {
|
||||||
"description": "Grant UCI access for luci-app-partexp",
|
"description": "Grant UCI access for luci-app-partexp",
|
||||||
"read": {
|
"read": {
|
||||||
"file": {
|
"ubus": {
|
||||||
"/etc/init.d/partexp": [ "exec" ],
|
"file": ["exec", "list", "stat", "read"],
|
||||||
"/var/partexp": ["read"],
|
"uci": [ "*" ],
|
||||||
"/etc/partexppath": ["read"],
|
"partexp": ["*"]
|
||||||
"/etc/partexp/": ["read"]
|
}
|
||||||
},
|
|
||||||
"uci": [ "partexp" ]
|
|
||||||
},
|
},
|
||||||
"write": {
|
"write": {
|
||||||
"file": {
|
"ubus": {
|
||||||
"/var/partexp": ["write"],
|
"partexp": ["*"],
|
||||||
"/etc/partexppath": ["write"],
|
"file": ["write"],
|
||||||
"/etc/partexp/": ["write"]
|
"uci": ["*"]
|
||||||
|
}
|
||||||
},
|
|
||||||
"uci": [ "partexp" ]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
include $(TOPDIR)/rules.mk
|
include $(TOPDIR)/rules.mk
|
||||||
|
|
||||||
PKG_NAME:=lucky
|
PKG_NAME:=lucky
|
||||||
PKG_VERSION:=2.20.2
|
PKG_VERSION:=2.24.0
|
||||||
PKG_RELEASE:=1
|
PKG_RELEASE:=1
|
||||||
PKGARCH:=all
|
PKGARCH:=all
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ PKG_LICENSE_FILES:=LICENSE
|
|||||||
PKG_MAINTAINER:=GDY666 <gdy666@foxmail.com>
|
PKG_MAINTAINER:=GDY666 <gdy666@foxmail.com>
|
||||||
|
|
||||||
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
|
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
|
||||||
PKG_HASH:=544d800e234d9348870edfdaf615a068779674ed1731beb4ff73e00376b15121
|
PKG_HASH:=d7409d3ab8742e2b7fa39339fe3b10edb969e01e177ea5c6f2c8b1776ce2593c
|
||||||
|
|
||||||
include $(INCLUDE_DIR)/package.mk
|
include $(INCLUDE_DIR)/package.mk
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user