Merge 956b9cbd7f ("Merge tag 'kbuild-fixes-7.0-1' of git://git.kernel.org/pub/scm/linux/kernel/git/kbuild/linux") into android-mainline

Steps on the way to v7.0-rc1

Change-Id: Iac3830657c4b3bf75c8d8f6f3c2fec245f7babcf
Signed-off-by: Carlos Llamas <cmllamas@google.com>
This commit is contained in:
Carlos Llamas authored and android-test-infra-autosubmit@system.gserviceaccount.com committed 2026-04-20 01:11:47 -07:00
commit 09656f44f8
66 files changed
+3916 -2101

No files matched your search

@@ -260,6 +260,17 @@ mode to off when the CPU is in any one of the available idle states. This may
help performance of a sibling CPU at the expense of a slightly higher wakeup
latency for the idle CPU.
The ``table`` argument allows customization of idle state latency and target
residency. The syntax is a comma-separated list of ``name:latency:residency``
entries, where ``name`` is the idle state name, ``latency`` is the exit latency
in microseconds, and ``residency`` is the target residency in microseconds. It
is not necessary to specify all idle states; only those to be customized. For
example, ``C1:1:3,C6:50:100`` sets the exit latency and target residency for
C1 and C6 to 1/3 and 50/100 microseconds, respectively. Remaining idle states
keep their default values. The driver verifies that deeper idle states have
higher latency and target residency than shallower ones. Also, target
residency cannot be smaller than exit latency. If any of these conditions is
not met, the driver ignores the entire ``table`` parameter.
.. _intel-idle-core-and-package-idle-states:
+3 -3
View File
@@ -753,9 +753,9 @@ Note: the mount options index=off,nfs_export=on are conflicting for a
read-write mount and will result in an error.
Note: the mount option uuid=off can be used to replace UUID of the underlying
filesystem in file handles with null, and effectively disable UUID checks. This
filesystem in file handles with null, in order to relax the UUID checks. This
can be useful in case the underlying disk is copied and the UUID of this copy
is changed. This is only applicable if all lower/upper/work directories are on
is changed. This is only applicable if all lower directories are on
the same filesystem, otherwise it will fallback to normal behaviour.
@@ -769,7 +769,7 @@ controlled by the "uuid" mount option, which supports these values:
UUID of overlayfs is null. fsid is taken from upper most filesystem.
- "off":
UUID of overlayfs is null. fsid is taken from upper most filesystem.
UUID of underlying layers is ignored.
UUID of underlying layers is ignored and null used instead.
- "on":
UUID of overlayfs is generated and used to report a unique fsid.
UUID is stored in xattr "trusted.overlay.uuid", making overlayfs fsid
+10 -1
View File
@@ -1525,6 +1525,15 @@ ifneq ($(wildcard $(resolve_btfids_O)),)
$(Q)$(MAKE) -sC $(srctree)/tools/bpf/resolve_btfids O=$(resolve_btfids_O) clean
endif
PHONY += objtool_clean
objtool_O = $(abspath $(objtree))/tools/objtool
objtool_clean:
ifneq ($(wildcard $(objtool_O)),)
$(Q)$(MAKE) -sC $(abs_srctree)/tools/objtool O=$(objtool_O) srctree=$(abs_srctree) clean
endif
tools/: FORCE
$(Q)mkdir -p $(objtree)/tools
$(Q)$(MAKE) O=$(abspath $(objtree)) subdir=tools -C $(srctree)/tools/
@@ -1696,7 +1705,7 @@ vmlinuxclean:
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/link-vmlinux.sh clean
$(Q)$(if $(ARCH_POSTLINK), $(MAKE) -f $(ARCH_POSTLINK) clean)
clean: archclean vmlinuxclean resolve_btfids_clean
clean: archclean vmlinuxclean resolve_btfids_clean objtool_clean
# mrproper - Delete all generated files, including .config
#
+1
View File
@@ -2116,6 +2116,7 @@ static void acpi_video_bus_remove(struct platform_device *pdev)
kfree(video->attached_array);
kfree(video);
device->driver_data = NULL;
}
static int __init is_i740(struct pci_dev *dev)
+1 -8
View File
@@ -1066,9 +1066,6 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
struct acpi_device *device = battery->device;
struct power_supply *old;
if (!battery)
return;
guard(mutex)(&battery->update_lock);
old = battery->bat;
@@ -1274,13 +1271,9 @@ fail:
static void acpi_battery_remove(struct platform_device *pdev)
{
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
struct acpi_battery *battery = platform_get_drvdata(pdev);
if (!device || !battery)
return;
acpi_dev_remove_notify_handler(device, ACPI_ALL_NOTIFY,
acpi_dev_remove_notify_handler(battery->device, ACPI_ALL_NOTIFY,
acpi_battery_notify);
device_init_wakeup(&pdev->dev, false);
+12 -8
View File
@@ -170,7 +170,7 @@ static struct platform_driver acpi_button_driver = {
struct acpi_button {
struct acpi_device *adev;
struct platform_device *pdev;
struct device *dev; /* physical button device */
unsigned int type;
struct input_dev *input;
char phys[32]; /* for input device */
@@ -398,7 +398,7 @@ static int acpi_lid_update_state(struct acpi_button *button,
return state;
if (state && signal_wakeup)
acpi_pm_wakeup_event(&button->pdev->dev);
acpi_pm_wakeup_event(button->dev);
return acpi_lid_notify_state(button, state);
}
@@ -455,7 +455,7 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data)
return;
}
acpi_pm_wakeup_event(&button->pdev->dev);
acpi_pm_wakeup_event(button->dev);
if (button->suspended || event == ACPI_BUTTON_NOTIFY_WAKE)
return;
@@ -550,7 +550,7 @@ static int acpi_button_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, button);
button->pdev = pdev;
button->dev = &pdev->dev;
button->adev = device;
button->input = input = input_allocate_device();
if (!input) {
@@ -625,6 +625,8 @@ static int acpi_button_probe(struct platform_device *pdev)
goto err_remove_fs;
}
device_init_wakeup(button->dev, true);
switch (device->device_type) {
case ACPI_BUS_TYPE_POWER_BUTTON:
status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
@@ -655,11 +657,11 @@ static int acpi_button_probe(struct platform_device *pdev)
lid_device = device;
}
device_init_wakeup(&pdev->dev, true);
pr_info("%s [%s]\n", name, acpi_device_bid(device));
return 0;
err_input_unregister:
device_init_wakeup(button->dev, false);
input_unregister_device(input);
err_remove_fs:
acpi_button_remove_fs(button);
@@ -670,10 +672,10 @@ err_free_button:
static void acpi_button_remove(struct platform_device *pdev)
{
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
struct acpi_button *button = platform_get_drvdata(pdev);
struct acpi_device *adev = button->adev;
switch (device->device_type) {
switch (adev->device_type) {
case ACPI_BUS_TYPE_POWER_BUTTON:
acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
acpi_button_event);
@@ -683,7 +685,7 @@ static void acpi_button_remove(struct platform_device *pdev)
acpi_button_event);
break;
default:
acpi_remove_notify_handler(device->handle, ACPI_DEVICE_NOTIFY,
acpi_remove_notify_handler(adev->handle, ACPI_DEVICE_NOTIFY,
button->type == ACPI_BUTTON_TYPE_LID ?
acpi_lid_notify :
acpi_button_notify);
@@ -691,6 +693,8 @@ static void acpi_button_remove(struct platform_device *pdev)
}
acpi_os_wait_events_complete();
device_init_wakeup(button->dev, false);
acpi_button_remove_fs(button);
input_unregister_device(button->input);
kfree(button);
+2 -2
View File
@@ -362,7 +362,7 @@ static int send_pcc_cmd(int pcc_ss_id, u16 cmd)
end:
if (cmd == CMD_WRITE) {
if (unlikely(ret)) {
for_each_possible_cpu(i) {
for_each_online_cpu(i) {
struct cpc_desc *desc = per_cpu(cpc_desc_ptr, i);
if (!desc)
@@ -524,7 +524,7 @@ int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data)
else if (pdomain->coord_type == DOMAIN_COORD_TYPE_SW_ANY)
cpu_data->shared_type = CPUFREQ_SHARED_TYPE_ANY;
for_each_possible_cpu(i) {
for_each_online_cpu(i) {
if (i == cpu)
continue;
-2
View File
@@ -1754,12 +1754,10 @@ err:
static void acpi_ec_remove(struct platform_device *pdev)
{
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
struct acpi_ec *ec = platform_get_drvdata(pdev);
release_region(ec->data_addr, 1);
release_region(ec->command_addr, 1);
device->driver_data = NULL;
if (ec != boot_ec) {
ec_remove_handlers(ec);
acpi_ec_free(ec);
+13
View File
@@ -1113,6 +1113,19 @@ static const struct dmi_system_id dmi_leave_unused_power_resources_on[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE Click Mini L9W-B"),
},
},
{
/*
* THUNDEROBOT ZERO laptop: Due to its SSDT table bug, power
* resource 'PXP' will be shut down on initialization, making
* the NVMe #2 and the NVIDIA dGPU both unavailable (they're
* both controlled by 'PXP').
*/
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "THUNDEROBOT"),
DMI_MATCH(DMI_PRODUCT_NAME, "ZERO"),
}
},
{}
};
-2
View File
@@ -275,13 +275,11 @@ static int acpi_smbus_hc_probe(struct platform_device *pdev)
static void acpi_smbus_hc_remove(struct platform_device *pdev)
{
struct acpi_device *device = ACPI_COMPANION(&pdev->dev);
struct acpi_smb_hc *hc = platform_get_drvdata(pdev);
acpi_ec_remove_query_handler(hc->ec, hc->query_bit);
acpi_os_wait_events_complete();
kfree(hc);
device->driver_data = NULL;
}
module_platform_driver(acpi_smb_hc_driver);
+12
View File
@@ -81,6 +81,18 @@ static const struct override_status_id override_status_ids[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Mipad2"),
}),
/*
* Lenovo Yoga Book uses PWM2 for touch keyboard backlight control.
* It needs to be enabled only for the Android device version (YB1-X90*
* aka YETI-11); the Windows version (YB1-X91*) uses ACPI control
* methods.
*/
PRESENT_ENTRY_HID("80862289", "2", INTEL_ATOM_AIRMONT, {
DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"),
DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "YETI-11"),
}),
/*
* The INT0002 device is necessary to clear wakeup interrupt sources
* on Cherry Trail devices, without it we get nobody cared IRQ msgs.
+7 -4
View File
@@ -273,8 +273,10 @@ EXPORT_SYMBOL_GPL(dev_pm_set_dedicated_wake_irq_reverse);
* otherwise try to disable already disabled wakeirq. The wake-up interrupt
* starts disabled with IRQ_NOAUTOEN set.
*
* Should be only called from rpm_suspend() and rpm_resume() path.
* Caller must hold &dev->power.lock to change wirq->status
* Should be called from rpm_suspend(), rpm_resume(),
* pm_runtime_force_suspend() or pm_runtime_force_resume().
* Caller must hold &dev->power.lock or disable runtime PM to change
* wirq->status.
*/
void dev_pm_enable_wake_irq_check(struct device *dev,
bool can_change_status)
@@ -306,7 +308,8 @@ enable:
* @cond_disable: if set, also check WAKE_IRQ_DEDICATED_REVERSE
*
* Disables wake-up interrupt conditionally based on status.
* Should be only called from rpm_suspend() and rpm_resume() path.
* Should be called from rpm_suspend(), rpm_resume(),
* pm_runtime_force_suspend() or pm_runtime_force_resume().
*/
void dev_pm_disable_wake_irq_check(struct device *dev, bool cond_disable)
{
@@ -332,7 +335,7 @@ void dev_pm_disable_wake_irq_check(struct device *dev, bool cond_disable)
* enable wake IRQ after running ->runtime_suspend() which depends on
* WAKE_IRQ_DEDICATED_REVERSE.
*
* Should be only called from rpm_suspend() path.
* Should be called from rpm_suspend() or pm_runtime_force_suspend().
*/
void dev_pm_enable_wake_irq_complete(struct device *dev)
{
+10
View File
@@ -372,6 +372,16 @@ noinstr int cpuidle_enter_state(struct cpuidle_device *dev,
int cpuidle_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
bool *stop_tick)
{
/*
* If there is only a single idle state (or none), there is nothing
* meaningful for the governor to choose. Skip the governor and
* always use state 0 with the tick running.
*/
if (drv->state_count <= 1) {
*stop_tick = false;
return 0;
}
return cpuidle_curr_governor->select(drv, dev, stop_tick);
}
+1 -3
View File
@@ -50,9 +50,7 @@ static int haltpoll_select(struct cpuidle_driver *drv,
struct cpuidle_device *dev,
bool *stop_tick)
{
s64 latency_req = cpuidle_governor_latency_req(dev->cpu);
if (!drv->state_count || latency_req == 0) {
if (cpuidle_governor_latency_req(dev->cpu) == 0) {
*stop_tick = false;
return 0;
}
+1 -1
View File
@@ -281,7 +281,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
data->bucket = BUCKETS - 1;
}
if (drv->state_count <= 1 || latency_req == 0 ||
if (latency_req == 0 ||
((data->next_timer_ns < drv->states[1].target_residency_ns ||
latency_req < drv->states[1].exit_latency_ns) &&
!dev->states_usage[0].disable)) {
-6
View File
@@ -338,12 +338,6 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
*/
cpu_data->sleep_length_ns = KTIME_MAX;
/* Check if there is any choice in the first place. */
if (drv->state_count < 2) {
idx = 0;
goto out_tick;
}
if (!dev->states_usage[0].disable)
idx = 0;
+8 -13
View File
@@ -254,7 +254,7 @@ static void rapl_init_domains(struct rapl_package *rp);
static int rapl_read_data_raw(struct rapl_domain *rd,
enum rapl_primitives prim,
bool xlate, u64 *data,
bool atomic);
bool pmu_ctx);
static int rapl_write_data_raw(struct rapl_domain *rd,
enum rapl_primitives prim,
unsigned long long value);
@@ -832,7 +832,7 @@ prim_fixups(struct rapl_domain *rd, enum rapl_primitives prim)
*/
static int rapl_read_data_raw(struct rapl_domain *rd,
enum rapl_primitives prim, bool xlate, u64 *data,
bool atomic)
bool pmu_ctx)
{
u64 value;
enum rapl_primitives prim_fixed = prim_fixups(rd, prim);
@@ -854,7 +854,7 @@ static int rapl_read_data_raw(struct rapl_domain *rd,
ra.mask = rpi->mask;
if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra, atomic)) {
if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra, pmu_ctx)) {
pr_debug("failed to read reg 0x%llx for %s:%s\n", ra.reg.val, rd->rp->name, rd->name);
return -EIO;
}
@@ -1590,23 +1590,21 @@ static struct rapl_pmu rapl_pmu;
/* PMU helpers */
static int get_pmu_cpu(struct rapl_package *rp)
static void set_pmu_cpumask(struct rapl_package *rp, cpumask_var_t mask)
{
int cpu;
if (!rp->has_pmu)
return nr_cpu_ids;
return;
/* Only TPMI & MSR RAPL are supported for now */
if (rp->priv->type != RAPL_IF_TPMI && rp->priv->type != RAPL_IF_MSR)
return nr_cpu_ids;
return;
/* TPMI/MSR RAPL uses any CPU in the package for PMU */
for_each_online_cpu(cpu)
if (topology_physical_package_id(cpu) == rp->id)
return cpu;
return nr_cpu_ids;
cpumask_set_cpu(cpu, mask);
}
static bool is_rp_pmu_cpu(struct rapl_package *rp, int cpu)
@@ -1883,7 +1881,6 @@ static ssize_t cpumask_show(struct device *dev,
{
struct rapl_package *rp;
cpumask_var_t cpu_mask;
int cpu;
int ret;
if (!alloc_cpumask_var(&cpu_mask, GFP_KERNEL))
@@ -1895,9 +1892,7 @@ static ssize_t cpumask_show(struct device *dev,
/* Choose a cpu for each RAPL Package */
list_for_each_entry(rp, &rapl_packages, plist) {
cpu = get_pmu_cpu(rp);
if (cpu < nr_cpu_ids)
cpumask_set_cpu(cpu, cpu_mask);
set_pmu_cpumask(rp, cpu_mask);
}
cpus_read_unlock();
+5 -7
View File
@@ -110,16 +110,14 @@ static int rapl_cpu_down_prep(unsigned int cpu)
return 0;
}
static int rapl_msr_read_raw(int cpu, struct reg_action *ra, bool atomic)
static int rapl_msr_read_raw(int cpu, struct reg_action *ra, bool pmu_ctx)
{
/*
* When called from atomic-context (eg PMU event handler)
* perform MSR read directly using rdmsrq().
* When called from PMU context, perform MSR read directly using
* rdmsrq() without IPI overhead. Package-scoped MSRs are readable
* from any CPU in the package.
*/
if (atomic) {
if (unlikely(smp_processor_id() != cpu))
return -EIO;
if (pmu_ctx) {
rdmsrq(ra->reg.msr, ra->value);
goto out;
}
+1 -1
View File
@@ -157,7 +157,7 @@ static int parse_one_domain(struct tpmi_rapl_package *trp, u32 offset)
tpmi_domain_flags = tpmi_domain_header >> 32 & 0xffff;
if (tpmi_domain_version == TPMI_VERSION_INVALID) {
pr_warn(FW_BUG "Invalid version\n");
pr_debug("Invalid version, other instances may be valid\n");
return -ENODEV;
}
@@ -467,8 +467,11 @@ int proc_thermal_rfim_add(struct pci_dev *pdev, struct proc_thermal_device *proc
break;
}
ret = sysfs_create_group(&pdev->dev.kobj, &dlvr_attribute_group);
if (ret)
if (ret) {
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_FIVR)
sysfs_remove_group(&pdev->dev.kobj, &fivr_attribute_group);
return ret;
}
}
if (proc_priv->mmio_feature_mask & PROC_THERMAL_FEATURE_DVFS) {
+32 -15
View File
@@ -1000,7 +1000,8 @@ unsigned int ceph_define_write_size(struct address_space *mapping)
{
struct inode *inode = mapping->host;
struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode);
unsigned int wsize = i_blocksize(inode);
struct ceph_inode_info *ci = ceph_inode(inode);
unsigned int wsize = ci->i_layout.stripe_unit;
if (fsc->mount_options->wsize < wsize)
wsize = fsc->mount_options->wsize;
@@ -1283,16 +1284,16 @@ static inline int move_dirty_folio_in_page_array(struct address_space *mapping,
}
static
int ceph_process_folio_batch(struct address_space *mapping,
struct writeback_control *wbc,
struct ceph_writeback_ctl *ceph_wbc)
void ceph_process_folio_batch(struct address_space *mapping,
struct writeback_control *wbc,
struct ceph_writeback_ctl *ceph_wbc)
{
struct inode *inode = mapping->host;
struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode);
struct ceph_client *cl = fsc->client;
struct folio *folio = NULL;
unsigned i;
int rc = 0;
int rc;
for (i = 0; can_next_page_be_processed(ceph_wbc, i); i++) {
folio = ceph_wbc->fbatch.folios[i];
@@ -1322,12 +1323,10 @@ int ceph_process_folio_batch(struct address_space *mapping,
rc = ceph_check_page_before_write(mapping, wbc,
ceph_wbc, folio);
if (rc == -ENODATA) {
rc = 0;
folio_unlock(folio);
ceph_wbc->fbatch.folios[i] = NULL;
continue;
} else if (rc == -E2BIG) {
rc = 0;
folio_unlock(folio);
ceph_wbc->fbatch.folios[i] = NULL;
break;
@@ -1379,8 +1378,6 @@ int ceph_process_folio_batch(struct address_space *mapping,
}
ceph_wbc->processed_in_fbatch = i;
return rc;
}
static inline
@@ -1666,7 +1663,9 @@ retry:
tag_pages_for_writeback(mapping, ceph_wbc.index, ceph_wbc.end);
while (!has_writeback_done(&ceph_wbc)) {
ceph_wbc.locked_pages = 0;
BUG_ON(ceph_wbc.locked_pages);
BUG_ON(ceph_wbc.pages);
ceph_wbc.max_pages = ceph_wbc.wsize >> PAGE_SHIFT;
get_more_pages:
@@ -1684,10 +1683,8 @@ get_more_pages:
break;
process_folio_batch:
rc = ceph_process_folio_batch(mapping, wbc, &ceph_wbc);
ceph_process_folio_batch(mapping, wbc, &ceph_wbc);
ceph_shift_unused_folios_left(&ceph_wbc.fbatch);
if (rc)
goto release_folios;
/* did we get anything? */
if (!ceph_wbc.locked_pages)
@@ -2199,6 +2196,7 @@ int ceph_uninline_data(struct file *file)
struct ceph_osd_request *req = NULL;
struct ceph_cap_flush *prealloc_cf = NULL;
struct folio *folio = NULL;
struct ceph_snap_context *snapc = NULL;
u64 inline_version = CEPH_INLINE_NONE;
struct page *pages[1];
int err = 0;
@@ -2226,6 +2224,24 @@ int ceph_uninline_data(struct file *file)
if (inline_version == 1) /* initial version, no data */
goto out_uninline;
down_read(&fsc->mdsc->snap_rwsem);
spin_lock(&ci->i_ceph_lock);
if (__ceph_have_pending_cap_snap(ci)) {
struct ceph_cap_snap *capsnap =
list_last_entry(&ci->i_cap_snaps,
struct ceph_cap_snap,
ci_item);
snapc = ceph_get_snap_context(capsnap->context);
} else {
if (!ci->i_head_snapc) {
ci->i_head_snapc = ceph_get_snap_context(
ci->i_snap_realm->cached_context);
}
snapc = ceph_get_snap_context(ci->i_head_snapc);
}
spin_unlock(&ci->i_ceph_lock);
up_read(&fsc->mdsc->snap_rwsem);
folio = read_mapping_folio(inode->i_mapping, 0, file);
if (IS_ERR(folio)) {
err = PTR_ERR(folio);
@@ -2241,7 +2257,7 @@ int ceph_uninline_data(struct file *file)
req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout,
ceph_vino(inode), 0, &len, 0, 1,
CEPH_OSD_OP_CREATE, CEPH_OSD_FLAG_WRITE,
NULL, 0, 0, false);
snapc, 0, 0, false);
if (IS_ERR(req)) {
err = PTR_ERR(req);
goto out_unlock;
@@ -2257,7 +2273,7 @@ int ceph_uninline_data(struct file *file)
req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout,
ceph_vino(inode), 0, &len, 1, 3,
CEPH_OSD_OP_WRITE, CEPH_OSD_FLAG_WRITE,
NULL, ci->i_truncate_seq,
snapc, ci->i_truncate_seq,
ci->i_truncate_size, false);
if (IS_ERR(req)) {
err = PTR_ERR(req);
@@ -2320,6 +2336,7 @@ out_unlock:
folio_put(folio);
}
out:
ceph_put_snap_context(snapc);
ceph_free_cap_flush(prealloc_cf);
doutc(cl, "%llx.%llx inline_version %llu = %d\n",
ceph_vinop(inode), inline_version, err);
+16 -1
View File
@@ -2568,6 +2568,7 @@ static int ceph_zero_partial_object(struct inode *inode,
struct ceph_inode_info *ci = ceph_inode(inode);
struct ceph_fs_client *fsc = ceph_inode_to_fs_client(inode);
struct ceph_osd_request *req;
struct ceph_snap_context *snapc;
int ret = 0;
loff_t zero = 0;
int op;
@@ -2582,12 +2583,25 @@ static int ceph_zero_partial_object(struct inode *inode,
op = CEPH_OSD_OP_ZERO;
}
spin_lock(&ci->i_ceph_lock);
if (__ceph_have_pending_cap_snap(ci)) {
struct ceph_cap_snap *capsnap =
list_last_entry(&ci->i_cap_snaps,
struct ceph_cap_snap,
ci_item);
snapc = ceph_get_snap_context(capsnap->context);
} else {
BUG_ON(!ci->i_head_snapc);
snapc = ceph_get_snap_context(ci->i_head_snapc);
}
spin_unlock(&ci->i_ceph_lock);
req = ceph_osdc_new_request(&fsc->client->osdc, &ci->i_layout,
ceph_vino(inode),
offset, length,
0, 1, op,
CEPH_OSD_FLAG_WRITE,
NULL, 0, 0, false);
snapc, 0, 0, false);
if (IS_ERR(req)) {
ret = PTR_ERR(req);
goto out;
@@ -2601,6 +2615,7 @@ static int ceph_zero_partial_object(struct inode *inode,
ceph_osdc_put_request(req);
out:
ceph_put_snap_context(snapc);
return ret;
}
+282 -130
View File
@@ -91,7 +91,8 @@ static int attr_load_runs(struct ATTRIB *attr, struct ntfs_inode *ni,
* run_deallocate_ex - Deallocate clusters.
*/
static int run_deallocate_ex(struct ntfs_sb_info *sbi, struct runs_tree *run,
CLST vcn, CLST len, CLST *done, bool trim)
CLST vcn, CLST len, CLST *done, bool trim,
struct runs_tree *run_da)
{
int err = 0;
CLST vcn_next, vcn0 = vcn, lcn, clen, dn = 0;
@@ -120,6 +121,16 @@ failed:
if (sbi) {
/* mark bitmap range [lcn + clen) as free and trim clusters. */
mark_as_free_ex(sbi, lcn, clen, trim);
if (run_da) {
CLST da_len;
if (!run_remove_range(run_da, vcn, clen,
&da_len)) {
err = -ENOMEM;
goto failed;
}
ntfs_sub_da(sbi, da_len);
}
}
dn += clen;
}
@@ -147,9 +158,10 @@ out:
* attr_allocate_clusters - Find free space, mark it as used and store in @run.
*/
int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,
CLST vcn, CLST lcn, CLST len, CLST *pre_alloc,
enum ALLOCATE_OPT opt, CLST *alen, const size_t fr,
CLST *new_lcn, CLST *new_len)
struct runs_tree *run_da, CLST vcn, CLST lcn,
CLST len, CLST *pre_alloc, enum ALLOCATE_OPT opt,
CLST *alen, const size_t fr, CLST *new_lcn,
CLST *new_len)
{
int err;
CLST flen, vcn0 = vcn, pre = pre_alloc ? *pre_alloc : 0;
@@ -166,6 +178,12 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,
continue;
}
if (err == -ENOSPC && new_len && vcn - vcn0) {
/* Keep already allocated clusters. */
*alen = vcn - vcn0;
return 0;
}
if (err)
goto out;
@@ -179,12 +197,21 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,
/* Add new fragment into run storage. */
if (!run_add_entry(run, vcn, lcn, flen, opt & ALLOCATE_MFT)) {
undo_alloc:
/* Undo last 'ntfs_look_for_free_space' */
mark_as_free_ex(sbi, lcn, len, false);
err = -ENOMEM;
goto out;
}
if (run_da) {
CLST da_len;
if (!run_remove_range(run_da, vcn, flen, &da_len)) {
goto undo_alloc;
}
ntfs_sub_da(sbi, da_len);
}
if (opt & ALLOCATE_ZERO) {
u8 shift = sbi->cluster_bits - SECTOR_SHIFT;
@@ -199,7 +226,7 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,
vcn += flen;
if (flen >= len || (opt & ALLOCATE_MFT) ||
(fr && run->count - cnt >= fr)) {
(opt & ALLOCATE_ONE_FR) || (fr && run->count - cnt >= fr)) {
*alen = vcn - vcn0;
return 0;
}
@@ -210,7 +237,8 @@ int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,
out:
/* Undo 'ntfs_look_for_free_space' */
if (vcn - vcn0) {
run_deallocate_ex(sbi, run, vcn0, vcn - vcn0, NULL, false);
run_deallocate_ex(sbi, run, vcn0, vcn - vcn0, NULL, false,
run_da);
run_truncate(run, vcn0);
}
@@ -275,7 +303,7 @@ int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr,
} else {
const char *data = resident_data(attr);
err = attr_allocate_clusters(sbi, run, 0, 0, len, NULL,
err = attr_allocate_clusters(sbi, run, NULL, 0, 0, len, NULL,
ALLOCATE_DEF, &alen, 0, NULL,
NULL);
if (err)
@@ -391,7 +419,7 @@ static int attr_set_size_res(struct ntfs_inode *ni, struct ATTRIB *attr,
}
/*
* attr_set_size - Change the size of attribute.
* attr_set_size_ex - Change the size of attribute.
*
* Extend:
* - Sparse/compressed: No allocated clusters.
@@ -399,24 +427,28 @@ static int attr_set_size_res(struct ntfs_inode *ni, struct ATTRIB *attr,
* Shrink:
* - No deallocate if @keep_prealloc is set.
*/
int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
const __le16 *name, u8 name_len, struct runs_tree *run,
u64 new_size, const u64 *new_valid, bool keep_prealloc,
struct ATTRIB **ret)
int attr_set_size_ex(struct ntfs_inode *ni, enum ATTR_TYPE type,
const __le16 *name, u8 name_len, struct runs_tree *run,
u64 new_size, const u64 *new_valid, bool keep_prealloc,
struct ATTRIB **ret, bool no_da)
{
int err = 0;
struct ntfs_sb_info *sbi = ni->mi.sbi;
u8 cluster_bits = sbi->cluster_bits;
bool is_mft = ni->mi.rno == MFT_REC_MFT && type == ATTR_DATA &&
!name_len;
u64 old_valid, old_size, old_alloc, new_alloc, new_alloc_tmp;
u64 old_valid, old_size, old_alloc, new_alloc_tmp;
u64 new_alloc = 0;
struct ATTRIB *attr = NULL, *attr_b;
struct ATTR_LIST_ENTRY *le, *le_b;
struct mft_inode *mi, *mi_b;
CLST alen, vcn, lcn, new_alen, old_alen, svcn, evcn;
CLST next_svcn, pre_alloc = -1, done = 0;
bool is_ext, is_bad = false;
bool is_ext = false, is_bad = false;
bool dirty = false;
struct runs_tree *run_da = run == &ni->file.run ? &ni->file.run_da :
NULL;
bool da = !is_mft && sbi->options->delalloc && run_da && !no_da;
u32 align;
struct MFT_REC *rec;
@@ -448,8 +480,11 @@ again:
is_ext = is_attr_ext(attr_b);
align = sbi->cluster_size;
if (is_ext)
if (is_ext) {
align <<= attr_b->nres.c_unit;
keep_prealloc = false;
da = false;
}
old_valid = le64_to_cpu(attr_b->nres.valid_size);
old_size = le64_to_cpu(attr_b->nres.data_size);
@@ -467,6 +502,37 @@ again_1:
goto ok;
}
if (da &&
(vcn = old_alen + run_len(&ni->file.run_da), new_alen > vcn)) {
/* Resize up normal file. Delay new clusters allocation. */
alen = new_alen - vcn;
if (ntfs_check_free_space(sbi, alen, 0, true)) {
if (!run_add_entry(&ni->file.run_da, vcn, SPARSE_LCN,
alen, false)) {
err = -ENOMEM;
goto out;
}
ntfs_add_da(sbi, alen);
goto ok1;
}
}
if (!keep_prealloc && run_da && run_da->count &&
(vcn = run_get_max_vcn(run_da), new_alen < vcn)) {
/* Shrink delayed clusters. */
/* Try to remove fragment from delay allocated run. */
if (!run_remove_range(run_da, new_alen, vcn - new_alen,
&alen)) {
err = -ENOMEM;
goto out;
}
ntfs_sub_da(sbi, alen);
}
vcn = old_alen - 1;
svcn = le64_to_cpu(attr_b->nres.svcn);
@@ -572,7 +638,8 @@ add_alloc_in_same_attr_seg:
} else {
/* ~3 bytes per fragment. */
err = attr_allocate_clusters(
sbi, run, vcn, lcn, to_allocate, &pre_alloc,
sbi, run, run_da, vcn, lcn, to_allocate,
&pre_alloc,
is_mft ? ALLOCATE_MFT : ALLOCATE_DEF, &alen,
is_mft ? 0 :
(sbi->record_size -
@@ -751,14 +818,14 @@ pack_runs:
mi_b->dirty = dirty = true;
err = run_deallocate_ex(sbi, run, vcn, evcn - vcn + 1, &dlen,
true);
true, run_da);
if (err)
goto out;
if (is_ext) {
/* dlen - really deallocated clusters. */
le64_sub_cpu(&attr_b->nres.total_size,
((u64)dlen << cluster_bits));
(u64)dlen << cluster_bits);
}
run_truncate(run, vcn);
@@ -813,14 +880,14 @@ ok1:
if (((type == ATTR_DATA && !name_len) ||
(type == ATTR_ALLOC && name == I30_NAME))) {
/* Update inode_set_bytes. */
if (attr_b->non_res) {
new_alloc = le64_to_cpu(attr_b->nres.alloc_size);
if (inode_get_bytes(&ni->vfs_inode) != new_alloc) {
inode_set_bytes(&ni->vfs_inode, new_alloc);
dirty = true;
}
if (attr_b->non_res &&
inode_get_bytes(&ni->vfs_inode) != new_alloc) {
inode_set_bytes(&ni->vfs_inode, new_alloc);
dirty = true;
}
i_size_write(&ni->vfs_inode, new_size);
/* Don't forget to update duplicate information in parent. */
if (dirty) {
ni->ni_flags |= NI_FLAG_UPDATE_PARENT;
@@ -861,7 +928,7 @@ restore_run:
is_bad = true;
undo_1:
run_deallocate_ex(sbi, run, vcn, alen, NULL, false);
run_deallocate_ex(sbi, run, vcn, alen, NULL, false, run_da);
run_truncate(run, vcn);
out:
@@ -884,43 +951,74 @@ bad_inode:
* - new allocated clusters are zeroed via blkdev_issue_zeroout.
*/
int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
CLST *len, bool *new, bool zero)
CLST *len, bool *new, bool zero, void **res, bool no_da)
{
int err = 0;
struct runs_tree *run = &ni->file.run;
struct ntfs_sb_info *sbi;
u8 cluster_bits;
struct ATTRIB *attr, *attr_b;
struct ATTR_LIST_ENTRY *le, *le_b;
struct mft_inode *mi, *mi_b;
CLST hint, svcn, to_alloc, evcn1, next_svcn, asize, end, vcn0, alen;
CLST alloc, evcn;
unsigned fr;
u64 total_size, total_size0;
int step = 0;
int err;
if (new)
*new = false;
if (res)
*res = NULL;
/* Try to find in cache. */
down_read(&ni->file.run_lock);
if (!run_lookup_entry(run, vcn, lcn, len, NULL))
if (!no_da && run_lookup_entry(&ni->file.run_da, vcn, lcn, len, NULL)) {
/* The requested vcn is delay allocated. */
*lcn = DELALLOC_LCN;
} else if (run_lookup_entry(&ni->file.run, vcn, lcn, len, NULL)) {
/* The requested vcn is known in current run. */
} else {
*len = 0;
}
up_read(&ni->file.run_lock);
if (*len && (*lcn != SPARSE_LCN || !new))
return 0; /* Fast normal way without allocation. */
/* No cluster in cache or we need to allocate cluster in hole. */
sbi = ni->mi.sbi;
cluster_bits = sbi->cluster_bits;
ni_lock(ni);
down_write(&ni->file.run_lock);
/* Repeat the code above (under write lock). */
if (!run_lookup_entry(run, vcn, lcn, len, NULL))
err = attr_data_get_block_locked(ni, vcn, clen, lcn, len, new, zero,
res, no_da);
up_write(&ni->file.run_lock);
ni_unlock(ni);
return err;
}
/*
* attr_data_get_block_locked - Helper for attr_data_get_block.
*/
int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen,
CLST *lcn, CLST *len, bool *new, bool zero,
void **res, bool no_da)
{
int err = 0;
struct ntfs_sb_info *sbi = ni->mi.sbi;
struct runs_tree *run = &ni->file.run;
struct runs_tree *run_da = &ni->file.run_da;
bool da = sbi->options->delalloc && !no_da;
u8 cluster_bits;
struct ATTRIB *attr, *attr_b;
struct ATTR_LIST_ENTRY *le, *le_b;
struct mft_inode *mi, *mi_b;
CLST hint, svcn, to_alloc, evcn1, next_svcn, asize, end, vcn0;
CLST alloc, evcn;
unsigned fr;
u64 total_size, total_size0;
int step;
again:
if (da && run_lookup_entry(run_da, vcn, lcn, len, NULL)) {
/* The requested vcn is delay allocated. */
*lcn = DELALLOC_LCN;
} else if (run_lookup_entry(run, vcn, lcn, len, NULL)) {
/* The requested vcn is known in current run. */
} else {
*len = 0;
}
if (*len) {
if (*lcn != SPARSE_LCN || !new)
@@ -929,6 +1027,9 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
clen = *len;
}
cluster_bits = sbi->cluster_bits;
step = 0;
le_b = NULL;
attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL, &mi_b);
if (!attr_b) {
@@ -937,8 +1038,15 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
}
if (!attr_b->non_res) {
u32 data_size = le32_to_cpu(attr_b->res.data_size);
*lcn = RESIDENT_LCN;
*len = 1;
*len = data_size;
if (res && data_size) {
*res = kmemdup(resident_data(attr_b), data_size,
GFP_KERNEL);
if (!*res)
err = -ENOMEM;
}
goto out;
}
@@ -948,7 +1056,7 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
err = -EINVAL;
} else {
*len = 1;
*lcn = SPARSE_LCN;
*lcn = EOF_LCN;
}
goto out;
}
@@ -1026,7 +1134,8 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
to_alloc = ((vcn0 + clen + clst_per_frame - 1) & cmask) - vcn;
if (fr < clst_per_frame)
fr = clst_per_frame;
zero = true;
if (vcn != vcn0)
zero = true;
/* Check if 'vcn' and 'vcn0' in different attribute segments. */
if (vcn < svcn || evcn1 <= vcn) {
@@ -1043,11 +1152,38 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
if (err)
goto out;
}
da = false; /* no delalloc for compressed file. */
}
if (vcn + to_alloc > asize)
to_alloc = asize - vcn;
if (da) {
CLST rlen1, rlen2;
if (!ntfs_check_free_space(sbi, to_alloc, 0, true)) {
err = ni_allocate_da_blocks_locked(ni);
if (err)
goto out;
/* Layout of records may be changed. Start again without 'da'. */
da = false;
goto again;
}
/* run_add_entry consolidates existed ranges. */
rlen1 = run_len(run_da);
if (!run_add_entry(run_da, vcn, SPARSE_LCN, to_alloc, false)) {
err = -ENOMEM;
goto out;
}
rlen2 = run_len(run_da);
/* new added delay clusters = rlen2 - rlen1. */
ntfs_add_da(sbi, rlen2 - rlen1);
*len = to_alloc;
*lcn = DELALLOC_LCN;
goto ok;
}
/* Get the last LCN to allocate from. */
hint = 0;
@@ -1062,18 +1198,19 @@ int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
}
/* Allocate and zeroout new clusters. */
err = attr_allocate_clusters(sbi, run, vcn, hint + 1, to_alloc, NULL,
zero ? ALLOCATE_ZERO : ALLOCATE_DEF, &alen,
fr, lcn, len);
err = attr_allocate_clusters(sbi, run, run_da, vcn, hint + 1, to_alloc,
NULL,
zero ? ALLOCATE_ZERO : ALLOCATE_ONE_FR,
len, fr, lcn, len);
if (err)
goto out;
*new = true;
step = 1;
end = vcn + alen;
end = vcn + *len;
/* Save 'total_size0' to restore if error. */
total_size0 = le64_to_cpu(attr_b->nres.total_size);
total_size = total_size0 + ((u64)alen << cluster_bits);
total_size = total_size0 + ((u64)*len << cluster_bits);
if (vcn != vcn0) {
if (!run_lookup_entry(run, vcn0, lcn, len, NULL)) {
@@ -1139,7 +1276,7 @@ repack:
* in 'ni_insert_nonresident'.
* Return in advance -ENOSPC here if there are no free cluster and no free MFT.
*/
if (!ntfs_check_for_free_space(sbi, 1, 1)) {
if (!ntfs_check_free_space(sbi, 1, 1, false)) {
/* Undo step 1. */
err = -ENOSPC;
goto undo1;
@@ -1224,8 +1361,6 @@ out:
/* Too complex to restore. */
_ntfs_bad_inode(&ni->vfs_inode);
}
up_write(&ni->file.run_lock);
ni_unlock(ni);
return err;
@@ -1234,41 +1369,14 @@ undo1:
attr_b->nres.total_size = cpu_to_le64(total_size0);
inode_set_bytes(&ni->vfs_inode, total_size0);
if (run_deallocate_ex(sbi, run, vcn, alen, NULL, false) ||
!run_add_entry(run, vcn, SPARSE_LCN, alen, false) ||
if (run_deallocate_ex(sbi, run, vcn, *len, NULL, false, run_da) ||
!run_add_entry(run, vcn, SPARSE_LCN, *len, false) ||
mi_pack_runs(mi, attr, run, max(end, evcn1) - svcn)) {
_ntfs_bad_inode(&ni->vfs_inode);
}
goto out;
}
int attr_data_read_resident(struct ntfs_inode *ni, struct folio *folio)
{
u64 vbo;
struct ATTRIB *attr;
u32 data_size;
size_t len;
attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL, NULL);
if (!attr)
return -EINVAL;
if (attr->non_res)
return E_NTFS_NONRESIDENT;
vbo = folio->index << PAGE_SHIFT;
data_size = le32_to_cpu(attr->res.data_size);
if (vbo > data_size)
len = 0;
else
len = min(data_size - vbo, folio_size(folio));
folio_fill_tail(folio, 0, resident_data(attr) + vbo, len);
folio_mark_uptodate(folio);
return 0;
}
int attr_data_write_resident(struct ntfs_inode *ni, struct folio *folio)
{
u64 vbo;
@@ -1285,7 +1393,7 @@ int attr_data_write_resident(struct ntfs_inode *ni, struct folio *folio)
return E_NTFS_NONRESIDENT;
}
vbo = folio->index << PAGE_SHIFT;
vbo = folio_pos(folio);
data_size = le32_to_cpu(attr->res.data_size);
if (vbo < data_size) {
char *data = resident_data(attr);
@@ -1354,19 +1462,27 @@ int attr_load_runs_range(struct ntfs_inode *ni, enum ATTR_TYPE type,
CLST vcn;
CLST vcn_last = (to - 1) >> cluster_bits;
CLST lcn, clen;
int err;
int err = 0;
int retry = 0;
for (vcn = from >> cluster_bits; vcn <= vcn_last; vcn += clen) {
if (!run_lookup_entry(run, vcn, &lcn, &clen, NULL)) {
err = attr_load_runs_vcn(ni, type, name, name_len, run,
vcn);
if (err)
return err;
clen = 0; /* Next run_lookup_entry(vcn) must be success. */
if (run_lookup_entry(run, vcn, &lcn, &clen, NULL)) {
retry = 0;
continue;
}
if (retry) {
err = -EINVAL;
break;
}
err = attr_load_runs_vcn(ni, type, name, name_len, run, vcn);
if (err)
break;
clen = 0; /* Next run_lookup_entry(vcn) must be success. */
retry++;
}
return 0;
return err;
}
#ifdef CONFIG_NTFS3_LZX_XPRESS
@@ -1689,7 +1805,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size,
if (len < clst_data) {
err = run_deallocate_ex(sbi, run, vcn + len, clst_data - len,
NULL, true);
NULL, true, NULL);
if (err)
goto out;
@@ -1709,7 +1825,7 @@ int attr_allocate_frame(struct ntfs_inode *ni, CLST frame, size_t compr_size,
hint = -1;
}
err = attr_allocate_clusters(sbi, run, vcn + clst_data,
err = attr_allocate_clusters(sbi, run, NULL, vcn + clst_data,
hint + 1, len - clst_data, NULL,
ALLOCATE_DEF, &alen, 0, NULL,
NULL);
@@ -1864,6 +1980,7 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
CLST vcn, end;
u64 valid_size, data_size, alloc_size, total_size;
u32 mask;
u64 i_size;
__le16 a_flags;
if (!bytes)
@@ -1879,52 +1996,79 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
return 0;
}
data_size = le64_to_cpu(attr_b->nres.data_size);
alloc_size = le64_to_cpu(attr_b->nres.alloc_size);
a_flags = attr_b->flags;
if (is_attr_ext(attr_b)) {
total_size = le64_to_cpu(attr_b->nres.total_size);
mask = (sbi->cluster_size << attr_b->nres.c_unit) - 1;
} else {
total_size = alloc_size;
mask = sbi->cluster_mask;
}
if ((vbo & mask) || (bytes & mask)) {
mask = is_attr_ext(attr_b) ?
((sbi->cluster_size << attr_b->nres.c_unit) - 1) :
sbi->cluster_mask;
if ((vbo | bytes) & mask) {
/* Allow to collapse only cluster aligned ranges. */
return -EINVAL;
}
if (vbo > data_size)
/* i_size - size of file with delay allocated clusters. */
i_size = ni->vfs_inode.i_size;
if (vbo > i_size)
return -EINVAL;
down_write(&ni->file.run_lock);
if (vbo + bytes >= data_size) {
u64 new_valid = min(ni->i_valid, vbo);
if (vbo + bytes >= i_size) {
valid_size = min(ni->i_valid, vbo);
/* Simple truncate file at 'vbo'. */
truncate_setsize(&ni->vfs_inode, vbo);
err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, vbo,
&new_valid, true, NULL);
&valid_size, true);
if (!err && new_valid < ni->i_valid)
ni->i_valid = new_valid;
if (!err && valid_size < ni->i_valid)
ni->i_valid = valid_size;
goto out;
}
/*
* Enumerate all attribute segments and collapse.
*/
alen = alloc_size >> sbi->cluster_bits;
vcn = vbo >> sbi->cluster_bits;
len = bytes >> sbi->cluster_bits;
end = vcn + len;
dealloc = 0;
done = 0;
/*
* Check delayed clusters.
*/
if (ni->file.run_da.count) {
struct runs_tree *run_da = &ni->file.run_da;
if (run_is_mapped_full(run_da, vcn, end - 1)) {
/*
* The requested range is full in delayed clusters.
*/
err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, run,
i_size - bytes, NULL, false,
NULL, true);
goto out;
}
/* Collapse request crosses real and delayed clusters. */
err = ni_allocate_da_blocks_locked(ni);
if (err)
goto out;
/* Layout of records maybe changed. */
le_b = NULL;
attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL,
&mi_b);
if (!attr_b || !attr_b->non_res) {
err = -ENOENT;
goto out;
}
}
data_size = le64_to_cpu(attr_b->nres.data_size);
alloc_size = le64_to_cpu(attr_b->nres.alloc_size);
total_size = is_attr_ext(attr_b) ?
le64_to_cpu(attr_b->nres.total_size) :
alloc_size;
alen = alloc_size >> sbi->cluster_bits;
a_flags = attr_b->flags;
svcn = le64_to_cpu(attr_b->nres.svcn);
evcn1 = le64_to_cpu(attr_b->nres.evcn) + 1;
@@ -1947,6 +2091,9 @@ int attr_collapse_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
goto out;
}
/*
* Enumerate all attribute segments and collapse.
*/
for (;;) {
CLST vcn1, eat, next_svcn;
@@ -1974,13 +2121,13 @@ check_seg:
vcn1 = vcn + done; /* original vcn in attr/run. */
eat = min(end, evcn1) - vcn1;
err = run_deallocate_ex(sbi, run, vcn1, eat, &dealloc, true);
err = run_deallocate_ex(sbi, run, vcn1, eat, &dealloc, true,
NULL);
if (err)
goto out;
if (svcn + eat < evcn1) {
/* Collapse a part of this attribute segment. */
if (!run_collapse_range(run, vcn1, eat, done)) {
err = -ENOMEM;
goto out;
@@ -2161,9 +2308,9 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size)
bytes = alloc_size;
bytes -= vbo;
if ((vbo & mask) || (bytes & mask)) {
if ((vbo | bytes) & mask) {
/* We have to zero a range(s). */
if (frame_size == NULL) {
if (!frame_size) {
/* Caller insists range is aligned. */
return -EINVAL;
}
@@ -2222,7 +2369,8 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size)
* Calculate how many clusters there are.
* Don't do any destructive actions.
*/
err = run_deallocate_ex(NULL, run, vcn1, zero, &hole2, false);
err = run_deallocate_ex(NULL, run, vcn1, zero, &hole2, false,
NULL);
if (err)
goto done;
@@ -2260,7 +2408,8 @@ int attr_punch_hole(struct ntfs_inode *ni, u64 vbo, u64 bytes, u32 *frame_size)
}
/* Real deallocate. Should not fail. */
run_deallocate_ex(sbi, &run2, vcn1, zero, &hole, true);
run_deallocate_ex(sbi, &run2, vcn1, zero, &hole, true,
&ni->file.run_da);
next_attr:
/* Free all allocated memory. */
@@ -2372,7 +2521,7 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
return -EINVAL;
}
if ((vbo & mask) || (bytes & mask)) {
if ((vbo | bytes) & mask) {
/* Allow to insert only frame aligned ranges. */
return -EINVAL;
}
@@ -2391,7 +2540,7 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
if (!attr_b->non_res) {
err = attr_set_size(ni, ATTR_DATA, NULL, 0, run,
data_size + bytes, NULL, false, NULL);
data_size + bytes, NULL, false);
le_b = NULL;
attr_b = ni_find_attr(ni, NULL, &le_b, ATTR_DATA, NULL, 0, NULL,
@@ -2414,7 +2563,7 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
goto done;
}
/* Resident files becomes nonresident. */
/* Resident file becomes nonresident. */
data_size = le64_to_cpu(attr_b->nres.data_size);
alloc_size = le64_to_cpu(attr_b->nres.alloc_size);
}
@@ -2451,10 +2600,13 @@ int attr_insert_range(struct ntfs_inode *ni, u64 vbo, u64 bytes)
if (err)
goto out;
if (!run_insert_range(run, vcn, len)) {
err = -ENOMEM;
err = run_insert_range(run, vcn, len);
if (err)
goto out;
err = run_insert_range_da(&ni->file.run_da, vcn, len);
if (err)
goto out;
}
/* Try to pack in current record as much as possible. */
err = mi_pack_runs(mi, attr, run, evcn1 + len - svcn);
+13 -4
View File
@@ -52,6 +52,11 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
if (!attr->non_res) {
lsize = le32_to_cpu(attr->res.data_size);
if (!lsize) {
err = -EINVAL;
goto out;
}
/* attr is resident: lsize < record_size (1K or 4K) */
le = kvmalloc(al_aligned(lsize), GFP_KERNEL);
if (!le) {
@@ -66,6 +71,10 @@ int ntfs_load_attr_list(struct ntfs_inode *ni, struct ATTRIB *attr)
u16 run_off = le16_to_cpu(attr->nres.run_off);
lsize = le64_to_cpu(attr->nres.data_size);
if (!lsize) {
err = -EINVAL;
goto out;
}
run_init(&ni->attr_list.run);
@@ -336,8 +345,8 @@ int al_add_le(struct ntfs_inode *ni, enum ATTR_TYPE type, const __le16 *name,
le->id = id;
memcpy(le->name, name, sizeof(short) * name_len);
err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
&new_size, true, &attr);
err = attr_set_size_ex(ni, ATTR_LIST, NULL, 0, &al->run, new_size,
&new_size, true, &attr, false);
if (err) {
/* Undo memmove above. */
memmove(le, Add2Ptr(le, sz), old_size - off);
@@ -395,8 +404,8 @@ int al_update(struct ntfs_inode *ni, int sync)
* Attribute list increased on demand in al_add_le.
* Attribute list decreased here.
*/
err = attr_set_size(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL,
false, &attr);
err = attr_set_size_ex(ni, ATTR_LIST, NULL, 0, &al->run, al->size, NULL,
false, &attr, false);
if (err)
goto out;
+17
View File
@@ -508,6 +508,8 @@ static int wnd_rescan(struct wnd_bitmap *wnd)
size_t wpos, wbit, iw, vbo;
struct buffer_head *bh = NULL;
CLST lcn, clen;
struct file_ra_state *ra;
struct address_space *mapping = sb->s_bdev->bd_mapping;
wnd->uptodated = 0;
wnd->extent_max = 0;
@@ -516,6 +518,13 @@ static int wnd_rescan(struct wnd_bitmap *wnd)
vbo = 0;
/* Allocate in memory instead of stack. Not critical if failed. */
ra = kzalloc(sizeof(*ra), GFP_NOFS);
if (ra) {
file_ra_state_init(ra, mapping);
ra->ra_pages = (wnd->nbits / 8 + PAGE_SIZE - 1) >> PAGE_SHIFT;
}
for (iw = 0; iw < wnd->nwnd; iw++) {
if (iw + 1 == wnd->nwnd)
wbits = wnd->bits_last;
@@ -552,6 +561,13 @@ static int wnd_rescan(struct wnd_bitmap *wnd)
len = ((u64)clen << cluster_bits) - off;
}
if (ra) {
pgoff_t idx = lbo >> PAGE_SHIFT;
if (!ra_has_index(ra, idx))
page_cache_sync_readahead(mapping, ra, NULL,
idx, 1);
}
bh = ntfs_bread(sb, lbo >> sb->s_blocksize_bits);
if (!bh) {
err = -EIO;
@@ -638,6 +654,7 @@ next_wnd:
}
out:
kfree(ra);
return err;
}
+76 -32
View File
@@ -393,33 +393,77 @@ static int ntfs_read_hdr(struct ntfs_sb_info *sbi, struct ntfs_inode *ni,
* ntfs_readdir - file_operations::iterate_shared
*
* Use non sorted enumeration.
* We have an example of broken volume where sorted enumeration
* counts each name twice.
* Sorted enumeration may result infinite loop if names tree contains loop.
*/
static int ntfs_readdir(struct file *file, struct dir_context *ctx)
{
const struct INDEX_ROOT *root;
u64 vbo;
size_t bit;
loff_t eod;
int err = 0;
struct inode *dir = file_inode(file);
struct ntfs_inode *ni = ntfs_i(dir);
struct super_block *sb = dir->i_sb;
struct ntfs_sb_info *sbi = sb->s_fs_info;
loff_t i_size = i_size_read(dir);
u32 pos = ctx->pos;
u64 pos = ctx->pos;
u8 *name = NULL;
struct indx_node *node = NULL;
u8 index_bits = ni->dir.index_bits;
size_t max_bit = i_size >> ni->dir.index_bits;
loff_t eod = i_size + sbi->record_size;
/* Name is a buffer of PATH_MAX length. */
static_assert(NTFS_NAME_LEN * 4 < PATH_MAX);
eod = i_size + sbi->record_size;
if (!pos) {
/*
* ni->dir.version increments each directory change.
* Save the initial value of ni->dir.version.
*/
file->private_data = (void *)ni->dir.version;
}
if (pos >= eod)
return 0;
if (pos >= eod) {
if (file->private_data == (void *)ni->dir.version) {
/* No changes since first readdir. */
return 0;
}
/*
* Handle directories that changed after the initial readdir().
*
* Some user space code implements recursive removal like this instead
* of calling rmdir(2) directly:
*
* fd = opendir(path);
* while ((dent = readdir(fd)))
* unlinkat(dirfd(fd), dent->d_name, 0);
* closedir(fd);
*
* POSIX leaves unspecified what readdir() should return once the
* directory has been modified after opendir()/rewinddir(), so this
* pattern is not guaranteed to work on all filesystems or platforms.
*
* In ntfs3 the internal name tree may be reshaped while entries are
* being removed, so there is no stable anchor for continuing a
* single-pass walk based on the original readdir() order.
*
* In practice some widely used tools (for example certain rm(1)
* implementations) have used this readdir()/unlink() loop, and some
* filesystems behave in a way that effectively makes it work in the
* common case.
*
* The code below follows that practice and tries to provide
* "rmdir-like" behaviour for such callers on ntfs3, even though the
* situation is not strictly defined by the APIs.
*
* Apple documents the same readdir()/unlink() issue and a workaround
* for HFS file systems in:
* https://web.archive.org/web/20220122122948/https:/support.apple.com/kb/TA21420?locale=en_US
*/
ctx->pos = pos = 3;
file->private_data = (void *)ni->dir.version;
}
if (!dir_emit_dots(file, ctx))
return 0;
@@ -454,58 +498,58 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx)
if (pos >= sbi->record_size) {
bit = (pos - sbi->record_size) >> index_bits;
} else {
/*
* Add each name from root in 'ctx'.
*/
err = ntfs_read_hdr(sbi, ni, &root->ihdr, 0, pos, name, ctx);
if (err)
goto out;
bit = 0;
}
if (!i_size) {
ctx->pos = eod;
goto out;
}
for (;;) {
vbo = (u64)bit << index_bits;
if (vbo >= i_size) {
ctx->pos = eod;
goto out;
}
/*
* Enumerate indexes until the end of dir.
*/
for (; bit < max_bit; bit += 1) {
/* Get the next used index. */
err = indx_used_bit(&ni->dir, ni, &bit);
if (err)
goto out;
if (bit == MINUS_ONE_T) {
ctx->pos = eod;
goto out;
/* no more used indexes. end of dir. */
break;
}
vbo = (u64)bit << index_bits;
if (vbo >= i_size) {
if (bit >= max_bit) {
/* Corrupted directory. */
err = -EINVAL;
goto out;
}
err = indx_read(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
&node);
err = indx_read_ra(&ni->dir, ni, bit << ni->dir.idx2vbn_bits,
&node, &file->f_ra);
if (err)
goto out;
/*
* Add each name from index in 'ctx'.
*/
err = ntfs_read_hdr(sbi, ni, &node->index->ihdr,
vbo + sbi->record_size, pos, name, ctx);
((u64)bit << index_bits) + sbi->record_size,
pos, name, ctx);
if (err)
goto out;
bit += 1;
}
out:
kfree(name);
put_indx_node(node);
if (err == 1) {
if (!err) {
/* End of directory. */
ctx->pos = eod;
} else if (err == 1) {
/* 'ctx' is full. */
err = 0;
} else if (err == -ENOENT) {
@@ -624,7 +668,7 @@ const struct file_operations ntfs_dir_operations = {
.llseek = generic_file_llseek,
.read = generic_read_dir,
.iterate_shared = ntfs_readdir,
.fsync = generic_file_fsync,
.fsync = ntfs_file_fsync,
.open = ntfs_file_open,
.unlocked_ioctl = ntfs_ioctl,
#ifdef CONFIG_COMPAT
+344 -255
View File
@@ -15,6 +15,7 @@
#include <linux/fiemap.h>
#include <linux/fileattr.h>
#include <linux/filelock.h>
#include <linux/iomap.h>
#include "debug.h"
#include "ntfs.h"
@@ -26,6 +27,38 @@
*/
#define NTFS3_IOC_SHUTDOWN _IOR('X', 125, __u32)
/*
* Helper for ntfs_should_use_dio.
*/
static u32 ntfs_dio_alignment(struct inode *inode)
{
struct ntfs_inode *ni = ntfs_i(inode);
if (is_resident(ni)) {
/* Check delalloc. */
if (!ni->file.run_da.count)
return 0;
}
/* In most cases this is bdev_logical_block_size(bdev). */
return ni->mi.sbi->bdev_blocksize;
}
/*
* Returns %true if the given DIO request should be attempted with DIO, or
* %false if it should fall back to buffered I/O.
*/
static bool ntfs_should_use_dio(struct kiocb *iocb, struct iov_iter *iter)
{
struct inode *inode = file_inode(iocb->ki_filp);
u32 dio_align = ntfs_dio_alignment(inode);
if (!dio_align)
return false;
return IS_ALIGNED(iocb->ki_pos | iov_iter_alignment(iter), dio_align);
}
static int ntfs_ioctl_fitrim(struct ntfs_sb_info *sbi, unsigned long arg)
{
struct fstrim_range __user *user_range;
@@ -186,13 +219,10 @@ int ntfs_getattr(struct mnt_idmap *idmap, const struct path *path,
static int ntfs_extend_initialized_size(struct file *file,
struct ntfs_inode *ni,
const loff_t valid,
const loff_t new_valid)
{
struct inode *inode = &ni->vfs_inode;
struct address_space *mapping = inode->i_mapping;
struct ntfs_sb_info *sbi = inode->i_sb->s_fs_info;
loff_t pos = valid;
const loff_t valid = ni->i_valid;
int err;
if (valid >= new_valid)
@@ -203,142 +233,41 @@ static int ntfs_extend_initialized_size(struct file *file,
return 0;
}
WARN_ON(is_compressed(ni));
for (;;) {
u32 zerofrom, len;
struct folio *folio;
u8 bits;
CLST vcn, lcn, clen;
if (is_sparsed(ni)) {
bits = sbi->cluster_bits;
vcn = pos >> bits;
err = attr_data_get_block(ni, vcn, 1, &lcn, &clen, NULL,
false);
if (err)
goto out;
if (lcn == SPARSE_LCN) {
pos = ((loff_t)clen + vcn) << bits;
ni->i_valid = pos;
goto next;
}
}
zerofrom = pos & (PAGE_SIZE - 1);
len = PAGE_SIZE - zerofrom;
if (pos + len > new_valid)
len = new_valid - pos;
err = ntfs_write_begin(NULL, mapping, pos, len, &folio, NULL);
if (err)
goto out;
folio_zero_range(folio, zerofrom, folio_size(folio) - zerofrom);
err = ntfs_write_end(NULL, mapping, pos, len, len, folio, NULL);
if (err < 0)
goto out;
pos += len;
next:
if (pos >= new_valid)
break;
balance_dirty_pages_ratelimited(mapping);
cond_resched();
err = iomap_zero_range(inode, valid, new_valid - valid, NULL,
&ntfs_iomap_ops, &ntfs_iomap_folio_ops, NULL);
if (err) {
ni->i_valid = valid;
ntfs_inode_warn(inode,
"failed to extend initialized size to %llx.",
new_valid);
return err;
}
return 0;
out:
ni->i_valid = valid;
ntfs_inode_warn(inode, "failed to extend initialized size to %llx.",
new_valid);
return err;
}
/*
* ntfs_zero_range - Helper function for punch_hole.
*
* It zeroes a range [vbo, vbo_to).
*/
static int ntfs_zero_range(struct inode *inode, u64 vbo, u64 vbo_to)
static void ntfs_filemap_close(struct vm_area_struct *vma)
{
int err = 0;
struct address_space *mapping = inode->i_mapping;
u32 blocksize = i_blocksize(inode);
pgoff_t idx = vbo >> PAGE_SHIFT;
u32 from = vbo & (PAGE_SIZE - 1);
pgoff_t idx_end = (vbo_to + PAGE_SIZE - 1) >> PAGE_SHIFT;
loff_t page_off;
struct buffer_head *head, *bh;
u32 bh_next, bh_off, to;
sector_t iblock;
struct folio *folio;
bool dirty = false;
struct inode *inode = file_inode(vma->vm_file);
struct ntfs_inode *ni = ntfs_i(inode);
u64 from = (u64)vma->vm_pgoff << PAGE_SHIFT;
u64 to = min_t(u64, i_size_read(inode),
from + vma->vm_end - vma->vm_start);
for (; idx < idx_end; idx += 1, from = 0) {
page_off = (loff_t)idx << PAGE_SHIFT;
to = (page_off + PAGE_SIZE) > vbo_to ? (vbo_to - page_off) :
PAGE_SIZE;
iblock = page_off >> inode->i_blkbits;
folio = __filemap_get_folio(
mapping, idx, FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
mapping_gfp_constraint(mapping, ~__GFP_FS));
if (IS_ERR(folio))
return PTR_ERR(folio);
head = folio_buffers(folio);
if (!head)
head = create_empty_buffers(folio, blocksize, 0);
bh = head;
bh_off = 0;
do {
bh_next = bh_off + blocksize;
if (bh_next <= from || bh_off >= to)
continue;
if (!buffer_mapped(bh)) {
ntfs_get_block(inode, iblock, bh, 0);
/* Unmapped? It's a hole - nothing to do. */
if (!buffer_mapped(bh))
continue;
}
/* Ok, it's mapped. Make sure it's up-to-date. */
if (folio_test_uptodate(folio))
set_buffer_uptodate(bh);
else if (bh_read(bh, 0) < 0) {
err = -EIO;
folio_unlock(folio);
folio_put(folio);
goto out;
}
mark_buffer_dirty(bh);
} while (bh_off = bh_next, iblock += 1,
head != (bh = bh->b_this_page));
folio_zero_segment(folio, from, to);
dirty = true;
folio_unlock(folio);
folio_put(folio);
cond_resched();
}
out:
if (dirty)
if (ni->i_valid < to) {
ni->i_valid = to;
mark_inode_dirty(inode);
return err;
}
}
/* Copy of generic_file_vm_ops. */
static const struct vm_operations_struct ntfs_file_vm_ops = {
.close = ntfs_filemap_close,
.fault = filemap_fault,
.map_pages = filemap_map_pages,
.page_mkwrite = filemap_page_mkwrite,
};
/*
* ntfs_file_mmap_prepare - file_operations::mmap_prepare
*/
@@ -347,7 +276,6 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
struct file *file = desc->file;
struct inode *inode = file_inode(file);
struct ntfs_inode *ni = ntfs_i(inode);
u64 from = ((u64)desc->pgoff << PAGE_SHIFT);
bool rw = desc->vm_flags & VM_WRITE;
int err;
@@ -379,7 +307,8 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
}
if (rw) {
u64 to = min_t(loff_t, i_size_read(inode),
u64 from = (u64)desc->pgoff << PAGE_SHIFT;
u64 to = min_t(u64, i_size_read(inode),
from + vma_desc_size(desc));
if (is_sparsed(ni)) {
@@ -392,7 +321,8 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
for (; vcn < end; vcn += len) {
err = attr_data_get_block(ni, vcn, 1, &lcn,
&len, &new, true);
&len, &new, true,
NULL, false);
if (err)
goto out;
}
@@ -403,8 +333,7 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
err = -EAGAIN;
goto out;
}
err = ntfs_extend_initialized_size(file, ni,
ni->i_valid, to);
err = ntfs_extend_initialized_size(file, ni, to);
inode_unlock(inode);
if (err)
goto out;
@@ -412,6 +341,8 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc)
}
err = generic_file_mmap_prepare(desc);
if (!err && rw)
desc->vm_ops = &ntfs_file_vm_ops;
out:
return err;
}
@@ -432,55 +363,23 @@ static int ntfs_extend(struct inode *inode, loff_t pos, size_t count,
ntfs_set_state(ni->mi.sbi, NTFS_DIRTY_DIRTY);
if (end > inode->i_size) {
/*
* Normal files: increase file size, allocate space.
* Sparse/Compressed: increase file size. No space allocated.
*/
err = ntfs_set_size(inode, end);
if (err)
goto out;
}
if (extend_init && !is_compressed(ni)) {
err = ntfs_extend_initialized_size(file, ni, ni->i_valid, pos);
err = ntfs_extend_initialized_size(file, ni, pos);
if (err)
goto out;
} else {
err = 0;
}
if (file && is_sparsed(ni)) {
/*
* This code optimizes large writes to sparse file.
* TODO: merge this fragment with fallocate fragment.
*/
struct ntfs_sb_info *sbi = ni->mi.sbi;
CLST vcn = pos >> sbi->cluster_bits;
CLST cend = bytes_to_cluster(sbi, end);
CLST cend_v = bytes_to_cluster(sbi, ni->i_valid);
CLST lcn, clen;
bool new;
if (cend_v > cend)
cend_v = cend;
/*
* Allocate and zero new clusters.
* Zeroing these clusters may be too long.
*/
for (; vcn < cend_v; vcn += clen) {
err = attr_data_get_block(ni, vcn, cend_v - vcn, &lcn,
&clen, &new, true);
if (err)
goto out;
}
/*
* Allocate but not zero new clusters.
*/
for (; vcn < cend; vcn += clen) {
err = attr_data_get_block(ni, vcn, cend - vcn, &lcn,
&clen, &new, false);
if (err)
goto out;
}
}
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
mark_inode_dirty(inode);
@@ -504,53 +403,37 @@ out:
static int ntfs_truncate(struct inode *inode, loff_t new_size)
{
struct super_block *sb = inode->i_sb;
int err;
struct ntfs_inode *ni = ntfs_i(inode);
int err, dirty = 0;
u64 new_valid;
if (!S_ISREG(inode->i_mode))
return 0;
if (is_compressed(ni)) {
if (ni->i_valid > new_size)
ni->i_valid = new_size;
} else {
err = block_truncate_page(inode->i_mapping, new_size,
ntfs_get_block);
if (err)
return err;
}
new_valid = ntfs_up_block(sb, min_t(u64, ni->i_valid, new_size));
u64 new_valid = min_t(u64, ni->i_valid, new_size);
truncate_setsize(inode, new_size);
ni_lock(ni);
down_write(&ni->file.run_lock);
err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, new_size,
&new_valid, ni->mi.sbi->options->prealloc, NULL);
err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, &ni->file.run, new_size,
&new_valid, ni->mi.sbi->options->prealloc, NULL,
false);
up_write(&ni->file.run_lock);
if (new_valid < ni->i_valid)
ni->i_valid = new_valid;
ni->i_valid = new_valid;
ni_unlock(ni);
if (err)
return err;
ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE;
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
if (!IS_DIRSYNC(inode)) {
dirty = 1;
mark_inode_dirty(inode);
} else {
err = ntfs_sync_inode(inode);
if (err)
return err;
}
if (dirty)
mark_inode_dirty(inode);
return 0;
}
@@ -623,7 +506,7 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
if (mode & FALLOC_FL_PUNCH_HOLE) {
u32 frame_size;
loff_t mask, vbo_a, end_a, tmp;
loff_t mask, vbo_a, end_a, tmp, from;
err = filemap_write_and_wait_range(mapping, vbo_down,
LLONG_MAX);
@@ -643,24 +526,24 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
/* Process not aligned punch. */
err = 0;
if (end > i_size)
end = i_size;
mask = frame_size - 1;
vbo_a = (vbo + mask) & ~mask;
end_a = end & ~mask;
tmp = min(vbo_a, end);
if (tmp > vbo) {
err = ntfs_zero_range(inode, vbo, tmp);
from = min_t(loff_t, ni->i_valid, vbo);
/* Zero head of punch. */
if (tmp > from) {
err = iomap_zero_range(inode, from, tmp - from, NULL,
&ntfs_iomap_ops,
&ntfs_iomap_folio_ops, NULL);
if (err)
goto out;
}
if (vbo < end_a && end_a < end) {
err = ntfs_zero_range(inode, end_a, end);
if (err)
goto out;
}
/* Aligned punch_hole */
/* Aligned punch_hole. Deallocate clusters. */
if (end_a > vbo_a) {
ni_lock(ni);
err = attr_punch_hole(ni, vbo_a, end_a - vbo_a, NULL);
@@ -668,6 +551,15 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
if (err)
goto out;
}
/* Zero tail of punch. */
if (vbo < end_a && end_a < end) {
err = iomap_zero_range(inode, end_a, end - end_a, NULL,
&ntfs_iomap_ops,
&ntfs_iomap_folio_ops, NULL);
if (err)
goto out;
}
} else if (mode & FALLOC_FL_COLLAPSE_RANGE) {
/*
* Write tail of the last page before removed range since
@@ -765,17 +657,26 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
for (; vcn < cend_v; vcn += clen) {
err = attr_data_get_block(ni, vcn, cend_v - vcn,
&lcn, &clen, &new,
true);
true, NULL, false);
if (err)
goto out;
}
/*
* Moving up 'valid size'.
*/
err = ntfs_extend_initialized_size(
file, ni, (u64)cend_v << cluster_bits);
if (err)
goto out;
/*
* Allocate but not zero new clusters.
*/
for (; vcn < cend; vcn += clen) {
err = attr_data_get_block(ni, vcn, cend - vcn,
&lcn, &clen, &new,
false);
false, NULL, false);
if (err)
goto out;
}
@@ -786,10 +687,11 @@ static long ntfs_fallocate(struct file *file, int mode, loff_t vbo, loff_t len)
/* True - Keep preallocated. */
err = attr_set_size(ni, ATTR_DATA, NULL, 0,
&ni->file.run, i_size, &ni->i_valid,
true, NULL);
true);
ni_unlock(ni);
if (err)
goto out;
i_size_write(inode, i_size);
} else if (new_size > i_size) {
i_size_write(inode, new_size);
}
@@ -926,12 +828,18 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
struct ntfs_inode *ni = ntfs_i(inode);
size_t bytes = iov_iter_count(iter);
loff_t valid, i_size, vbo, end;
unsigned int dio_flags;
ssize_t err;
err = check_read_restriction(inode);
if (err)
return err;
if (!bytes)
return 0; /* skip atime */
if (is_compressed(ni)) {
if (iocb->ki_flags & IOCB_DIRECT) {
ntfs_inode_warn(
@@ -942,17 +850,63 @@ static ssize_t ntfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter)
file->f_ra.ra_pages = 0;
}
/* Check minimum alignment for dio. */
if (iocb->ki_flags & IOCB_DIRECT) {
struct super_block *sb = inode->i_sb;
struct ntfs_sb_info *sbi = sb->s_fs_info;
if ((iocb->ki_pos | iov_iter_alignment(iter)) &
sbi->bdev_blocksize_mask) {
iocb->ki_flags &= ~IOCB_DIRECT;
}
/* Fallback to buffered I/O if the inode does not support direct I/O. */
if (!(iocb->ki_flags & IOCB_DIRECT) ||
!ntfs_should_use_dio(iocb, iter)) {
iocb->ki_flags &= ~IOCB_DIRECT;
return generic_file_read_iter(iocb, iter);
}
return generic_file_read_iter(iocb, iter);
if (iocb->ki_flags & IOCB_NOWAIT) {
if (!inode_trylock_shared(inode))
return -EAGAIN;
} else {
inode_lock_shared(inode);
}
vbo = iocb->ki_pos;
end = vbo + bytes;
dio_flags = 0;
valid = ni->i_valid;
i_size = inode->i_size;
if (vbo < valid) {
if (valid < end) {
/* read cross 'valid' size. */
dio_flags |= IOMAP_DIO_FORCE_WAIT;
}
if (ni->file.run_da.count) {
/* Direct I/O is not compatible with delalloc. */
err = ni_allocate_da_blocks(ni);
if (err)
goto out;
}
err = iomap_dio_rw(iocb, iter, &ntfs_iomap_ops, NULL, dio_flags,
NULL, 0);
if (err <= 0)
goto out;
end = vbo + err;
if (valid < end) {
size_t to_zero = end - valid;
/* Fix iter. */
iov_iter_revert(iter, to_zero);
iov_iter_zero(to_zero, iter);
}
} else if (vbo < i_size) {
if (end > i_size)
bytes = i_size - vbo;
iov_iter_zero(bytes, iter);
iocb->ki_pos += bytes;
err = bytes;
}
out:
inode_unlock_shared(inode);
file_accessed(iocb->ki_filp);
return err;
}
/*
@@ -996,7 +950,7 @@ static int ntfs_get_frame_pages(struct address_space *mapping, pgoff_t index,
folio = __filemap_get_folio(mapping, index,
FGP_LOCK | FGP_ACCESSED | FGP_CREAT,
gfp_mask);
gfp_mask | __GFP_ZERO);
if (IS_ERR(folio)) {
while (npages--) {
folio = page_folio(pages[npages]);
@@ -1073,7 +1027,7 @@ static ssize_t ntfs_compress_write(struct kiocb *iocb, struct iov_iter *from)
off = valid & (frame_size - 1);
err = attr_data_get_block(ni, frame << NTFS_LZNT_CUNIT, 1, &lcn,
&clen, NULL, false);
&clen, NULL, false, NULL, false);
if (err)
goto out;
@@ -1265,6 +1219,9 @@ static int check_write_restriction(struct inode *inode)
return -EOPNOTSUPP;
}
if (unlikely(IS_IMMUTABLE(inode)))
return -EPERM;
return 0;
}
@@ -1276,8 +1233,7 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
struct ntfs_inode *ni = ntfs_i(inode);
ssize_t ret;
int err;
ssize_t ret, err;
if (!inode_trylock(inode)) {
if (iocb->ki_flags & IOCB_NOWAIT)
@@ -1315,15 +1271,75 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
if (ret)
goto out;
ret = is_compressed(ni) ? ntfs_compress_write(iocb, from) :
__generic_file_write_iter(iocb, from);
if (is_compressed(ni)) {
ret = ntfs_compress_write(iocb, from);
goto out;
}
/* Fallback to buffered I/O if the inode does not support direct I/O. */
if (!(iocb->ki_flags & IOCB_DIRECT) ||
!ntfs_should_use_dio(iocb, from)) {
iocb->ki_flags &= ~IOCB_DIRECT;
ret = iomap_file_buffered_write(iocb, from, &ntfs_iomap_ops,
&ntfs_iomap_folio_ops, NULL);
inode_unlock(inode);
if (likely(ret > 0))
ret = generic_write_sync(iocb, ret);
return ret;
}
if (ni->file.run_da.count) {
/* Direct I/O is not compatible with delalloc. */
ret = ni_allocate_da_blocks(ni);
if (ret)
goto out;
}
ret = iomap_dio_rw(iocb, from, &ntfs_iomap_ops, NULL, 0, NULL, 0);
if (ret == -ENOTBLK) {
/* Returns -ENOTBLK in case of a page invalidation failure for writes.*/
/* The callers needs to fall back to buffered I/O in this case. */
ret = 0;
}
if (ret >= 0 && iov_iter_count(from)) {
loff_t offset = iocb->ki_pos, endbyte;
iocb->ki_flags &= ~IOCB_DIRECT;
err = iomap_file_buffered_write(iocb, from, &ntfs_iomap_ops,
&ntfs_iomap_folio_ops, NULL);
if (err < 0) {
ret = err;
goto out;
}
/*
* We need to ensure that the pages within the page cache for
* the range covered by this I/O are written to disk and
* invalidated. This is in attempt to preserve the expected
* direct I/O semantics in the case we fallback to buffered I/O
* to complete off the I/O request.
*/
ret += err;
endbyte = offset + err - 1;
err = filemap_write_and_wait_range(inode->i_mapping, offset,
endbyte);
if (err) {
ret = err;
goto out;
}
invalidate_mapping_pages(inode->i_mapping, offset >> PAGE_SHIFT,
endbyte >> PAGE_SHIFT);
}
out:
inode_unlock(inode);
if (ret > 0)
ret = generic_write_sync(iocb, ret);
return ret;
}
@@ -1362,39 +1378,49 @@ int ntfs_file_open(struct inode *inode, struct file *file)
#endif
}
file->f_mode |= FMODE_NOWAIT | FMODE_CAN_ODIRECT;
return generic_file_open(inode, file);
}
/*
* ntfs_file_release - file_operations::release
*
* Called when an inode is released. Note that this is different
* from ntfs_file_open: open gets called at every open, but release
* gets called only when /all/ the files are closed.
*/
static int ntfs_file_release(struct inode *inode, struct file *file)
{
struct ntfs_inode *ni = ntfs_i(inode);
struct ntfs_sb_info *sbi = ni->mi.sbi;
int err = 0;
int err;
struct ntfs_inode *ni;
/* If we are last writer on the inode, drop the block reservation. */
if (sbi->options->prealloc &&
((file->f_mode & FMODE_WRITE) &&
atomic_read(&inode->i_writecount) == 1)
/*
* The only file when inode->i_fop = &ntfs_file_operations and
* init_rwsem(&ni->file.run_lock) is not called explicitly is MFT.
*
* Add additional check here.
*/
&& inode->i_ino != MFT_REC_MFT) {
if (!(file->f_mode & FMODE_WRITE) ||
atomic_read(&inode->i_writecount) != 1 ||
inode->i_ino == MFT_REC_MFT) {
return 0;
}
/* Close the last writer on the inode. */
ni = ntfs_i(inode);
/* Allocate delayed blocks (clusters). */
err = ni_allocate_da_blocks(ni);
if (err)
goto out;
if (ni->mi.sbi->options->prealloc) {
ni_lock(ni);
down_write(&ni->file.run_lock);
/* Deallocate preallocated. */
err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run,
i_size_read(inode), &ni->i_valid, false,
NULL);
inode->i_size, &ni->i_valid, false);
up_write(&ni->file.run_lock);
ni_unlock(ni);
}
out:
return err;
}
@@ -1411,16 +1437,30 @@ int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
if (unlikely(is_bad_ni(ni)))
return -EINVAL;
err = fiemap_prep(inode, fieinfo, start, &len, ~FIEMAP_FLAG_XATTR);
if (err)
return err;
if (is_compressed(ni)) {
/* Unfortunately cp -r incorrectly treats compressed clusters. */
ntfs_inode_warn(inode,
"fiemap is not supported for compressed file");
return -EOPNOTSUPP;
}
ni_lock(ni);
if (S_ISDIR(inode->i_mode)) {
/* TODO: add support for dirs (ATTR_ALLOC). */
ntfs_inode_warn(inode,
"fiemap is not supported for directories");
return -EOPNOTSUPP;
}
err = ni_fiemap(ni, fieinfo, start, len);
if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) {
ntfs_inode_warn(inode, "fiemap(xattr) is not supported");
return -EOPNOTSUPP;
}
ni_unlock(ni);
inode_lock_shared(inode);
err = iomap_fiemap(inode, fieinfo, start, len, &ntfs_iomap_ops);
inode_unlock_shared(inode);
return err;
}
@@ -1444,13 +1484,62 @@ static ssize_t ntfs_file_splice_write(struct pipe_inode_info *pipe,
/*
* ntfs_file_fsync - file_operations::fsync
*/
static int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
struct inode *inode = file_inode(file);
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
struct super_block *sb = inode->i_sb;
struct ntfs_sb_info *sbi = sb->s_fs_info;
int err, ret;
if (unlikely(ntfs3_forced_shutdown(sb)))
return -EIO;
return generic_file_fsync(file, start, end, datasync);
ret = file_write_and_wait_range(file, start, end);
if (ret)
return ret;
ret = write_inode_now(inode, !datasync);
if (!ret) {
ret = ni_write_parents(ntfs_i(inode), !datasync);
}
if (!ret) {
ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
ntfs_update_mftmirr(sbi);
}
err = sync_blockdev(sb->s_bdev);
if (unlikely(err && !ret))
ret = err;
if (!ret)
blkdev_issue_flush(sb->s_bdev);
return ret;
}
/*
* ntfs_llseek - file_operations::llseek
*/
static loff_t ntfs_llseek(struct file *file, loff_t offset, int whence)
{
struct inode *inode = file->f_mapping->host;
struct ntfs_inode *ni = ntfs_i(inode);
loff_t maxbytes = ntfs_get_maxbytes(ni);
loff_t ret;
if (whence == SEEK_DATA || whence == SEEK_HOLE) {
inode_lock_shared(inode);
/* Scan file for hole or data. */
ret = ni_seek_data_or_hole(ni, offset, whence == SEEK_DATA);
inode_unlock_shared(inode);
if (ret >= 0)
ret = vfs_setpos(file, ret, maxbytes);
} else {
ret = generic_file_llseek_size(file, offset, whence, maxbytes,
i_size_read(inode));
}
return ret;
}
// clang-format off
@@ -1464,7 +1553,7 @@ const struct inode_operations ntfs_file_inode_operations = {
};
const struct file_operations ntfs_file_operations = {
.llseek = generic_file_llseek,
.llseek = ntfs_llseek,
.read_iter = ntfs_file_read_iter,
.write_iter = ntfs_file_write_iter,
.unlocked_ioctl = ntfs_ioctl,
+201 -181
View File
@@ -123,6 +123,8 @@ void ni_clear(struct ntfs_inode *ni)
indx_clear(&ni->dir);
else {
run_close(&ni->file.run);
ntfs_sub_da(ni->mi.sbi, run_len(&ni->file.run_da));
run_close(&ni->file.run_da);
#ifdef CONFIG_NTFS3_LZX_XPRESS
if (ni->file.offs_folio) {
/* On-demand allocated page for offsets. */
@@ -1850,183 +1852,11 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
return REPARSE_LINK;
}
/*
* ni_fiemap - Helper for file_fiemap().
*
* Assumed ni_lock.
* TODO: Less aggressive locks.
*/
int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
__u64 vbo, __u64 len)
{
int err = 0;
struct ntfs_sb_info *sbi = ni->mi.sbi;
u8 cluster_bits = sbi->cluster_bits;
struct runs_tree run;
struct ATTRIB *attr;
CLST vcn = vbo >> cluster_bits;
CLST lcn, clen;
u64 valid = ni->i_valid;
u64 lbo, bytes;
u64 end, alloc_size;
size_t idx = -1;
u32 flags;
bool ok;
run_init(&run);
if (S_ISDIR(ni->vfs_inode.i_mode)) {
attr = ni_find_attr(ni, NULL, NULL, ATTR_ALLOC, I30_NAME,
ARRAY_SIZE(I30_NAME), NULL, NULL);
} else {
attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0, NULL,
NULL);
if (!attr) {
err = -EINVAL;
goto out;
}
if (is_attr_compressed(attr)) {
/* Unfortunately cp -r incorrectly treats compressed clusters. */
err = -EOPNOTSUPP;
ntfs_inode_warn(
&ni->vfs_inode,
"fiemap is not supported for compressed file (cp -r)");
goto out;
}
}
if (!attr || !attr->non_res) {
err = fiemap_fill_next_extent(
fieinfo, 0, 0,
attr ? le32_to_cpu(attr->res.data_size) : 0,
FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_LAST |
FIEMAP_EXTENT_MERGED);
goto out;
}
end = vbo + len;
alloc_size = le64_to_cpu(attr->nres.alloc_size);
if (end > alloc_size)
end = alloc_size;
while (vbo < end) {
if (idx == -1) {
ok = run_lookup_entry(&run, vcn, &lcn, &clen, &idx);
} else {
CLST vcn_next = vcn;
ok = run_get_entry(&run, ++idx, &vcn, &lcn, &clen) &&
vcn == vcn_next;
if (!ok)
vcn = vcn_next;
}
if (!ok) {
err = attr_load_runs_vcn(ni, attr->type,
attr_name(attr),
attr->name_len, &run, vcn);
if (err)
break;
ok = run_lookup_entry(&run, vcn, &lcn, &clen, &idx);
if (!ok) {
err = -EINVAL;
break;
}
}
if (!clen) {
err = -EINVAL; // ?
break;
}
if (lcn == SPARSE_LCN) {
vcn += clen;
vbo = (u64)vcn << cluster_bits;
continue;
}
flags = FIEMAP_EXTENT_MERGED;
if (S_ISDIR(ni->vfs_inode.i_mode)) {
;
} else if (is_attr_compressed(attr)) {
CLST clst_data;
err = attr_is_frame_compressed(ni, attr,
vcn >> attr->nres.c_unit,
&clst_data, &run);
if (err)
break;
if (clst_data < NTFS_LZNT_CLUSTERS)
flags |= FIEMAP_EXTENT_ENCODED;
} else if (is_attr_encrypted(attr)) {
flags |= FIEMAP_EXTENT_DATA_ENCRYPTED;
}
vbo = (u64)vcn << cluster_bits;
bytes = (u64)clen << cluster_bits;
lbo = (u64)lcn << cluster_bits;
vcn += clen;
if (vbo + bytes >= end)
bytes = end - vbo;
if (vbo + bytes <= valid) {
;
} else if (vbo >= valid) {
flags |= FIEMAP_EXTENT_UNWRITTEN;
} else {
/* vbo < valid && valid < vbo + bytes */
u64 dlen = valid - vbo;
if (vbo + dlen >= end)
flags |= FIEMAP_EXTENT_LAST;
err = fiemap_fill_next_extent(fieinfo, vbo, lbo, dlen,
flags);
if (err < 0)
break;
if (err == 1) {
err = 0;
break;
}
vbo = valid;
bytes -= dlen;
if (!bytes)
continue;
lbo += dlen;
flags |= FIEMAP_EXTENT_UNWRITTEN;
}
if (vbo + bytes >= end)
flags |= FIEMAP_EXTENT_LAST;
err = fiemap_fill_next_extent(fieinfo, vbo, lbo, bytes, flags);
if (err < 0)
break;
if (err == 1) {
err = 0;
break;
}
vbo += bytes;
}
out:
run_close(&run);
return err;
}
static struct page *ntfs_lock_new_page(struct address_space *mapping,
pgoff_t index, gfp_t gfp)
pgoff_t index, gfp_t gfp)
{
struct folio *folio = __filemap_get_folio(mapping, index,
FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
struct folio *folio = __filemap_get_folio(
mapping, index, FGP_LOCK | FGP_ACCESSED | FGP_CREAT, gfp);
struct page *page;
if (IS_ERR(folio))
@@ -2046,18 +1876,18 @@ static struct page *ntfs_lock_new_page(struct address_space *mapping,
}
/*
* ni_readpage_cmpr
* ni_read_folio_cmpr
*
* When decompressing, we typically obtain more than one page per reference.
* We inject the additional pages into the page cache.
*/
int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio)
int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio)
{
int err;
struct ntfs_sb_info *sbi = ni->mi.sbi;
struct address_space *mapping = folio->mapping;
pgoff_t index = folio->index;
u64 frame_vbo, vbo = (u64)index << PAGE_SHIFT;
pgoff_t index;
u64 frame_vbo, vbo = folio_pos(folio);
struct page **pages = NULL; /* Array of at most 16 pages. stack? */
u8 frame_bits;
CLST frame;
@@ -2107,7 +1937,9 @@ int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio)
pages[i] = pg;
}
ni_lock(ni);
err = ni_read_frame(ni, frame_vbo, pages, pages_per_frame, 0);
ni_unlock(ni);
out1:
for (i = 0; i < pages_per_frame; i++) {
@@ -2184,7 +2016,8 @@ int ni_decompress_file(struct ntfs_inode *ni)
for (vcn = vbo >> sbi->cluster_bits; vcn < end; vcn += clen) {
err = attr_data_get_block(ni, vcn, cend - vcn, &lcn,
&clen, &new, false);
&clen, &new, false, NULL,
false);
if (err)
goto out;
}
@@ -2405,7 +2238,7 @@ int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
struct runs_tree *run = &ni->file.run;
u64 valid_size = ni->i_valid;
u64 vbo_disk;
size_t unc_size;
size_t unc_size = 0;
u32 frame_size, i, ondisk_size;
struct page *pg;
struct ATTRIB *attr;
@@ -3001,6 +2834,134 @@ bool ni_is_dirty(struct inode *inode)
return false;
}
/*
* ni_seek_data_or_hole
*
* Helper function for ntfs_llseek( SEEK_DATA/SEEK_HOLE )
*/
loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data)
{
int err;
u8 cluster_bits = ni->mi.sbi->cluster_bits;
CLST vcn, lcn, clen;
loff_t vbo;
/* Enumerate all fragments. */
for (vcn = offset >> cluster_bits;; vcn += clen) {
err = attr_data_get_block(ni, vcn, 1, &lcn, &clen, NULL, false,
NULL, false);
if (err) {
return err;
}
if (lcn == RESIDENT_LCN) {
/* clen - resident size in bytes. clen == ni->vfs_inode.i_size */
if (offset >= clen) {
/* check eof. */
return -ENXIO;
}
if (data) {
return offset;
}
return clen;
}
if (lcn == EOF_LCN) {
if (data) {
return -ENXIO;
}
/* implicit hole at the end of file. */
return ni->vfs_inode.i_size;
}
if (data) {
/*
* Adjust the file offset to the next location in the file greater than
* or equal to offset containing data. If offset points to data, then
* the file offset is set to offset.
*/
if (lcn != SPARSE_LCN) {
vbo = (u64)vcn << cluster_bits;
return max(vbo, offset);
}
} else {
/*
* Adjust the file offset to the next hole in the file greater than or
* equal to offset. If offset points into the middle of a hole, then the
* file offset is set to offset. If there is no hole past offset, then the
* file offset is adjusted to the end of the file
* (i.e., there is an implicit hole at the end of any file).
*/
if (lcn == SPARSE_LCN &&
/* native compression hole begins at aligned vcn. */
(!(ni->std_fa & FILE_ATTRIBUTE_COMPRESSED) ||
!(vcn & (NTFS_LZNT_CLUSTERS - 1)))) {
vbo = (u64)vcn << cluster_bits;
return max(vbo, offset);
}
}
if (!clen) {
/* Corrupted file. */
return -EINVAL;
}
}
}
/*
* ni_write_parents
*
* Helper function for ntfs_file_fsync.
*/
int ni_write_parents(struct ntfs_inode *ni, int sync)
{
int err = 0;
struct ATTRIB *attr = NULL;
struct ATTR_LIST_ENTRY *le = NULL;
struct ntfs_sb_info *sbi = ni->mi.sbi;
struct super_block *sb = sbi->sb;
while ((attr = ni_find_attr(ni, attr, &le, ATTR_NAME, NULL, 0, NULL,
NULL))) {
struct inode *dir;
struct ATTR_FILE_NAME *fname;
fname = resident_data_ex(attr, SIZEOF_ATTRIBUTE_FILENAME);
if (!fname)
continue;
/* Check simple case when parent inode equals current inode. */
if (ino_get(&fname->home) == ni->vfs_inode.i_ino) {
if (MFT_REC_ROOT != ni->vfs_inode.i_ino) {
ntfs_set_state(sbi, NTFS_DIRTY_ERROR);
err = -EINVAL;
}
continue;
}
dir = ntfs_iget5(sb, &fname->home, NULL);
if (IS_ERR(dir)) {
ntfs_inode_warn(
&ni->vfs_inode,
"failed to open parent directory r=%lx to write",
(long)ino_get(&fname->home));
continue;
}
if (!is_bad_inode(dir)) {
int err2 = write_inode_now(dir, sync);
if (!err)
err = err2;
}
iput(dir);
}
return err;
}
/*
* ni_update_parent
*
@@ -3277,3 +3238,62 @@ out:
return 0;
}
/*
* Force to allocate all delay allocated clusters.
*/
int ni_allocate_da_blocks(struct ntfs_inode *ni)
{
int err;
ni_lock(ni);
down_write(&ni->file.run_lock);
err = ni_allocate_da_blocks_locked(ni);
up_write(&ni->file.run_lock);
ni_unlock(ni);
return err;
}
/*
* Force to allocate all delay allocated clusters.
*/
int ni_allocate_da_blocks_locked(struct ntfs_inode *ni)
{
int err;
if (!ni->file.run_da.count)
return 0;
if (is_sparsed(ni)) {
CLST vcn, lcn, clen, alen;
bool new;
/*
* Sparse file allocates clusters in 'attr_data_get_block_locked'
*/
while (run_get_entry(&ni->file.run_da, 0, &vcn, &lcn, &clen)) {
/* TODO: zero=true? */
err = attr_data_get_block_locked(ni, vcn, clen, &lcn,
&alen, &new, true,
NULL, true);
if (err)
break;
if (!new) {
err = -EINVAL;
break;
}
}
} else {
/*
* Normal file allocates clusters in 'attr_set_size'
*/
err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, &ni->file.run,
ni->vfs_inode.i_size, &ni->i_valid,
false, NULL, true);
}
return err;
}
+35 -30
View File
@@ -1074,6 +1074,8 @@ struct ntfs_log {
u32 client_undo_commit;
struct restart_info rst_info, rst_info2;
struct file_ra_state read_ahead;
};
static inline u32 lsn_to_vbo(struct ntfs_log *log, const u64 lsn)
@@ -1164,8 +1166,8 @@ static int read_log_page(struct ntfs_log *log, u32 vbo,
page_buf = page_off ? log->one_page_buf : *buffer;
err = ntfs_read_run_nb(ni->mi.sbi, &ni->file.run, page_vbo, page_buf,
log->page_size, NULL);
err = ntfs_read_run_nb_ra(ni->mi.sbi, &ni->file.run, page_vbo, page_buf,
log->page_size, NULL, &log->read_ahead);
if (err)
goto out;
@@ -3028,6 +3030,26 @@ static struct ATTRIB *attr_create_nonres_log(struct ntfs_sb_info *sbi,
return attr;
}
/*
* update_oa_attr - Synchronize OpenAttr's attribute pointer with modified attribute
* @oa2: OpenAttr structure in memory that needs to be updated
* @attr: Modified attribute from MFT record to duplicate
*
* Returns true on success, false on allocation failure.
*/
static bool update_oa_attr(struct OpenAttr *oa2, struct ATTRIB *attr)
{
void *p2;
p2 = kmemdup(attr, le32_to_cpu(attr->size), GFP_NOFS);
if (p2) {
kfree(oa2->attr);
oa2->attr = p2;
return true;
}
return false;
}
/*
* do_action - Common routine for the Redo and Undo Passes.
* @rlsn: If it is NULL then undo.
@@ -3251,15 +3273,8 @@ skip_load_parent:
le16_add_cpu(&rec->hard_links, 1);
oa2 = find_loaded_attr(log, attr, rno_base);
if (oa2) {
void *p2 = kmemdup(attr, le32_to_cpu(attr->size),
GFP_NOFS);
if (p2) {
// run_close(oa2->run1);
kfree(oa2->attr);
oa2->attr = p2;
}
}
if (oa2)
update_oa_attr(oa2, attr);
mi->dirty = true;
break;
@@ -3318,16 +3333,8 @@ move_data:
memmove(Add2Ptr(attr, aoff), data, dlen);
oa2 = find_loaded_attr(log, attr, rno_base);
if (oa2) {
void *p2 = kmemdup(attr, le32_to_cpu(attr->size),
GFP_NOFS);
if (p2) {
// run_close(&oa2->run0);
oa2->run1 = &oa2->run0;
kfree(oa2->attr);
oa2->attr = p2;
}
}
if (oa2 && update_oa_attr(oa2, attr))
oa2->run1 = &oa2->run0;
mi->dirty = true;
break;
@@ -3377,14 +3384,9 @@ move_data:
attr->nres.total_size = new_sz->total_size;
oa2 = find_loaded_attr(log, attr, rno_base);
if (oa2) {
void *p2 = kmemdup(attr, le32_to_cpu(attr->size),
GFP_NOFS);
if (p2) {
kfree(oa2->attr);
oa2->attr = p2;
}
}
if (oa2)
update_oa_attr(oa2, attr);
mi->dirty = true;
break;
@@ -3429,6 +3431,9 @@ move_data:
e1 = Add2Ptr(attr, le16_to_cpu(lrh->attr_off));
esize = le16_to_cpu(e1->size);
if (PtrOffset(e1, Add2Ptr(hdr, used)) < esize)
goto dirty_vol;
e2 = Add2Ptr(e1, esize);
memmove(e1, e2, PtrOffset(e2, Add2Ptr(hdr, used)));
@@ -5128,7 +5133,7 @@ commit_undo:
undo_action_done:
ntfs_update_mftmirr(sbi, 0);
ntfs_update_mftmirr(sbi);
sbi->flags &= ~NTFS_FLAGS_NEED_REPLAY;
+70 -42
View File
@@ -445,36 +445,59 @@ up_write:
}
/*
* ntfs_check_for_free_space
* ntfs_check_free_space
*
* Check if it is possible to allocate 'clen' clusters and 'mlen' Mft records
*/
bool ntfs_check_for_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST mlen)
bool ntfs_check_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST mlen,
bool da)
{
size_t free, zlen, avail;
struct wnd_bitmap *wnd;
CLST da_clusters = ntfs_get_da(sbi);
wnd = &sbi->used.bitmap;
down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_CLUSTERS);
free = wnd_zeroes(wnd);
if (free >= da_clusters) {
free -= da_clusters;
} else {
free = 0;
}
zlen = min_t(size_t, NTFS_MIN_MFT_ZONE, wnd_zone_len(wnd));
up_read(&wnd->rw_lock);
if (free < zlen + clen)
if (free < zlen + clen) {
return false;
}
avail = free - (zlen + clen);
wnd = &sbi->mft.bitmap;
down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_MFT);
free = wnd_zeroes(wnd);
zlen = wnd_zone_len(wnd);
up_read(&wnd->rw_lock);
/*
* When delalloc is active then keep in mind some reserved space.
* The worst case: 1 mft record per each ~500 clusters.
*/
if (da) {
/* 1 mft record per each 1024 clusters. */
mlen += da_clusters >> 10;
}
if (free >= zlen + mlen)
return true;
if (mlen || !avail) {
wnd = &sbi->mft.bitmap;
down_read_nested(&wnd->rw_lock, BITMAP_MUTEX_MFT);
free = wnd_zeroes(wnd);
zlen = wnd_zone_len(wnd);
up_read(&wnd->rw_lock);
return avail >= bytes_to_cluster(sbi, mlen << sbi->record_bits);
if (free < zlen + mlen &&
avail < bytes_to_cluster(sbi, mlen << sbi->record_bits)) {
return false;
}
}
return true;
}
/*
@@ -509,8 +532,8 @@ static int ntfs_extend_mft(struct ntfs_sb_info *sbi)
/* Step 1: Resize $MFT::DATA. */
down_write(&ni->file.run_lock);
err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run,
new_mft_bytes, NULL, false, &attr);
err = attr_set_size_ex(ni, ATTR_DATA, NULL, 0, &ni->file.run,
new_mft_bytes, NULL, false, &attr, false);
if (err) {
up_write(&ni->file.run_lock);
@@ -525,7 +548,7 @@ static int ntfs_extend_mft(struct ntfs_sb_info *sbi)
new_bitmap_bytes = ntfs3_bitmap_size(new_mft_total);
err = attr_set_size(ni, ATTR_BITMAP, NULL, 0, &sbi->mft.bitmap.run,
new_bitmap_bytes, &new_bitmap_bytes, true, NULL);
new_bitmap_bytes, &new_bitmap_bytes, true);
/* Refresh MFT Zone if necessary. */
down_write_nested(&sbi->used.bitmap.rw_lock, BITMAP_MUTEX_CLUSTERS);
@@ -843,9 +866,8 @@ int ntfs_refresh_zone(struct ntfs_sb_info *sbi)
/*
* ntfs_update_mftmirr - Update $MFTMirr data.
*/
void ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait)
void ntfs_update_mftmirr(struct ntfs_sb_info *sbi)
{
int err;
struct super_block *sb = sbi->sb;
u32 blocksize, bytes;
sector_t block1, block2;
@@ -875,9 +897,7 @@ void ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait)
return;
}
if (buffer_locked(bh2))
__wait_on_buffer(bh2);
wait_on_buffer(bh2);
lock_buffer(bh2);
memcpy(bh2->b_data, bh1->b_data, blocksize);
set_buffer_uptodate(bh2);
@@ -886,12 +906,7 @@ void ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait)
put_bh(bh1);
bh1 = NULL;
err = wait ? sync_dirty_buffer(bh2) : 0;
put_bh(bh2);
if (err)
return;
}
sbi->flags &= ~NTFS_FLAGS_MFTMIRR;
@@ -1069,9 +1084,7 @@ int ntfs_sb_write(struct super_block *sb, u64 lbo, size_t bytes,
return -ENOMEM;
}
if (buffer_locked(bh))
__wait_on_buffer(bh);
wait_on_buffer(bh);
lock_buffer(bh);
if (buf) {
memcpy(bh->b_data + off, buf, op);
@@ -1168,11 +1181,13 @@ struct buffer_head *ntfs_bread_run(struct ntfs_sb_info *sbi,
return ntfs_bread(sb, lbo >> sb->s_blocksize_bits);
}
int ntfs_read_run_nb(struct ntfs_sb_info *sbi, const struct runs_tree *run,
u64 vbo, void *buf, u32 bytes, struct ntfs_buffers *nb)
int ntfs_read_run_nb_ra(struct ntfs_sb_info *sbi, const struct runs_tree *run,
u64 vbo, void *buf, u32 bytes, struct ntfs_buffers *nb,
struct file_ra_state *ra)
{
int err;
struct super_block *sb = sbi->sb;
struct address_space *mapping = sb->s_bdev->bd_mapping;
u32 blocksize = sb->s_blocksize;
u8 cluster_bits = sbi->cluster_bits;
u32 off = vbo & sbi->cluster_mask;
@@ -1212,10 +1227,22 @@ int ntfs_read_run_nb(struct ntfs_sb_info *sbi, const struct runs_tree *run,
nb->bytes = bytes;
}
if (ra && !ra->ra_pages)
file_ra_state_init(ra, mapping);
for (;;) {
u32 len32 = len >= bytes ? bytes : len;
sector_t block = lbo >> sb->s_blocksize_bits;
if (ra) {
pgoff_t index = lbo >> PAGE_SHIFT;
if (!ra_has_index(ra, index)) {
page_cache_sync_readahead(mapping, ra, NULL,
index, 1);
ra->prev_pos = (loff_t)index << PAGE_SHIFT;
}
}
do {
u32 op = blocksize - off;
@@ -1252,6 +1279,12 @@ int ntfs_read_run_nb(struct ntfs_sb_info *sbi, const struct runs_tree *run,
} while (len32);
if (!run) {
err = -EINVAL;
goto out;
}
/* Get next fragment to read. */
vcn_next = vcn + clen;
if (!run_get_entry(run, ++idx, &vcn, &lcn, &clen) ||
vcn != vcn_next) {
@@ -1286,11 +1319,11 @@ out:
*
* Return: < 0 if error, 0 if ok, -E_NTFS_FIXUP if need to update fixups.
*/
int ntfs_read_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo,
struct NTFS_RECORD_HEADER *rhdr, u32 bytes,
struct ntfs_buffers *nb)
int ntfs_read_bh_ra(struct ntfs_sb_info *sbi, const struct runs_tree *run,
u64 vbo, struct NTFS_RECORD_HEADER *rhdr, u32 bytes,
struct ntfs_buffers *nb, struct file_ra_state *ra)
{
int err = ntfs_read_run_nb(sbi, run, vbo, rhdr, bytes, nb);
int err = ntfs_read_run_nb_ra(sbi, run, vbo, rhdr, bytes, nb, ra);
if (err)
return err;
@@ -1347,12 +1380,9 @@ int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo,
err = -ENOMEM;
goto out;
}
if (buffer_locked(bh))
__wait_on_buffer(bh);
wait_on_buffer(bh);
lock_buffer(bh);
if (!buffer_uptodate(bh))
{
if (!buffer_uptodate(bh)) {
memset(bh->b_data, 0, blocksize);
set_buffer_uptodate(bh);
}
@@ -1427,9 +1457,7 @@ int ntfs_write_bh(struct ntfs_sb_info *sbi, struct NTFS_RECORD_HEADER *rhdr,
if (op > bytes)
op = bytes;
if (buffer_locked(bh))
__wait_on_buffer(bh);
wait_on_buffer(bh);
lock_buffer(bh);
bh_data = bh->b_data + off;
@@ -2186,7 +2214,7 @@ int ntfs_insert_security(struct ntfs_sb_info *sbi,
if (new_sds_size > ni->vfs_inode.i_size) {
err = attr_set_size(ni, ATTR_DATA, SDS_NAME,
ARRAY_SIZE(SDS_NAME), &ni->file.run,
new_sds_size, &new_sds_size, false, NULL);
new_sds_size, &new_sds_size, false);
if (err)
goto out;
}
+27 -22
View File
@@ -252,9 +252,7 @@ static int bmp_buf_get(struct ntfs_index *indx, struct ntfs_inode *ni,
bbuf->bh = bh;
if (buffer_locked(bh))
__wait_on_buffer(bh);
wait_on_buffer(bh);
lock_buffer(bh);
sb = sbi->sb;
@@ -1028,17 +1026,18 @@ static int indx_write(struct ntfs_index *indx, struct ntfs_inode *ni,
}
/*
* indx_read
* indx_read_ra
*
* If ntfs_readdir calls this function
* inode is shared locked and no ni_lock.
* Use rw_semaphore for read/write access to alloc_run.
*/
int indx_read(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn,
struct indx_node **node)
int indx_read_ra(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn,
struct indx_node **node, struct file_ra_state *ra)
{
int err;
struct INDEX_BUFFER *ib;
struct ntfs_sb_info *sbi = ni->mi.sbi;
struct runs_tree *run = &indx->alloc_run;
struct rw_semaphore *lock = &indx->run_lock;
u64 vbo = (u64)vbn << indx->vbn2vbo_bits;
@@ -1064,7 +1063,7 @@ int indx_read(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn,
}
down_read(lock);
err = ntfs_read_bh(ni->mi.sbi, run, vbo, &ib->rhdr, bytes, &in->nb);
err = ntfs_read_bh_ra(sbi, run, vbo, &ib->rhdr, bytes, &in->nb, ra);
up_read(lock);
if (!err)
goto ok;
@@ -1084,7 +1083,7 @@ int indx_read(struct ntfs_index *indx, struct ntfs_inode *ni, CLST vbn,
goto out;
down_read(lock);
err = ntfs_read_bh(ni->mi.sbi, run, vbo, &ib->rhdr, bytes, &in->nb);
err = ntfs_read_bh_ra(sbi, run, vbo, &ib->rhdr, bytes, &in->nb, ra);
up_read(lock);
if (err == -E_NTFS_FIXUP)
goto ok;
@@ -1100,7 +1099,7 @@ ok:
}
if (err == -E_NTFS_FIXUP) {
ntfs_write_bh(ni->mi.sbi, &ib->rhdr, &in->nb, 0);
ntfs_write_bh(sbi, &ib->rhdr, &in->nb, 0);
err = 0;
}
@@ -1190,7 +1189,12 @@ int indx_find(struct ntfs_index *indx, struct ntfs_inode *ni,
return -EINVAL;
}
fnd_push(fnd, node, e);
err = fnd_push(fnd, node, e);
if (err) {
put_indx_node(node);
return err;
}
}
*entry = e;
@@ -1442,8 +1446,8 @@ static int indx_create_allocate(struct ntfs_index *indx, struct ntfs_inode *ni,
run_init(&run);
err = attr_allocate_clusters(sbi, &run, 0, 0, len, NULL, ALLOCATE_DEF,
&alen, 0, NULL, NULL);
err = attr_allocate_clusters(sbi, &run, NULL, 0, 0, len, NULL,
ALLOCATE_DEF, &alen, 0, NULL, NULL);
if (err)
goto out;
@@ -1527,8 +1531,7 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni,
/* Increase bitmap. */
err = attr_set_size(ni, ATTR_BITMAP, in->name, in->name_len,
&indx->bitmap_run,
ntfs3_bitmap_size(bit + 1), NULL, true,
NULL);
ntfs3_bitmap_size(bit + 1), NULL, true);
if (err)
goto out1;
}
@@ -1549,8 +1552,7 @@ static int indx_add_allocate(struct ntfs_index *indx, struct ntfs_inode *ni,
/* Increase allocation. */
err = attr_set_size(ni, ATTR_ALLOC, in->name, in->name_len,
&indx->alloc_run, data_size, &data_size, true,
NULL);
&indx->alloc_run, data_size, &data_size, true);
if (err) {
if (bmp)
goto out2;
@@ -1568,7 +1570,7 @@ out:
out2:
/* Ops. No space? */
attr_set_size(ni, ATTR_BITMAP, in->name, in->name_len,
&indx->bitmap_run, bmp_size, &bmp_size_v, false, NULL);
&indx->bitmap_run, bmp_size, &bmp_size_v, false);
out1:
return err;
@@ -1998,6 +2000,7 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
fnd->level - 1, fnd);
}
indx->version += 1;
out:
fnd_put(fnd_a);
out1:
@@ -2101,7 +2104,7 @@ static int indx_shrink(struct ntfs_index *indx, struct ntfs_inode *ni,
new_data = (u64)bit << indx->index_bits;
err = attr_set_size(ni, ATTR_ALLOC, in->name, in->name_len,
&indx->alloc_run, new_data, &new_data, false, NULL);
&indx->alloc_run, new_data, &new_data, false);
if (err)
return err;
@@ -2113,7 +2116,7 @@ static int indx_shrink(struct ntfs_index *indx, struct ntfs_inode *ni,
return 0;
err = attr_set_size(ni, ATTR_BITMAP, in->name, in->name_len,
&indx->bitmap_run, bpb, &bpb, false, NULL);
&indx->bitmap_run, bpb, &bpb, false);
return err;
}
@@ -2328,6 +2331,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
hdr = &root->ihdr;
e = fnd->root_de;
n = NULL;
ib = NULL;
}
e_size = le16_to_cpu(e->size);
@@ -2350,7 +2354,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
* Check to see if removing that entry made
* the leaf empty.
*/
if (ib_is_leaf(ib) && ib_is_empty(ib)) {
if (ib && ib_is_leaf(ib) && ib_is_empty(ib)) {
fnd_pop(fnd);
fnd_push(fnd2, n, e);
}
@@ -2598,7 +2602,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
in = &s_index_names[indx->type];
err = attr_set_size(ni, ATTR_ALLOC, in->name, in->name_len,
&indx->alloc_run, 0, NULL, false, NULL);
&indx->alloc_run, 0, NULL, false);
if (in->name == I30_NAME)
i_size_write(&ni->vfs_inode, 0);
@@ -2607,7 +2611,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
run_close(&indx->alloc_run);
err = attr_set_size(ni, ATTR_BITMAP, in->name, in->name_len,
&indx->bitmap_run, 0, NULL, false, NULL);
&indx->bitmap_run, 0, NULL, false);
err = ni_remove_attr(ni, ATTR_BITMAP, in->name, in->name_len,
false, NULL);
run_close(&indx->bitmap_run);
@@ -2645,6 +2649,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni,
mi->dirty = true;
}
indx->version += 1;
out:
fnd_put(fnd2);
out1:
+454 -390
View File
@@ -12,6 +12,7 @@
#include <linux/nls.h>
#include <linux/uio.h>
#include <linux/writeback.h>
#include <linux/iomap.h>
#include "debug.h"
#include "ntfs.h"
@@ -39,7 +40,7 @@ static struct inode *ntfs_read_mft(struct inode *inode,
u32 rp_fa = 0, asize, t32;
u16 roff, rsize, names = 0, links = 0;
const struct ATTR_FILE_NAME *fname = NULL;
const struct INDEX_ROOT *root;
const struct INDEX_ROOT *root = NULL;
struct REPARSE_DATA_BUFFER rp; // 0x18 bytes
u64 t64;
struct MFT_REC *rec;
@@ -166,9 +167,7 @@ next_attr:
std5 = Add2Ptr(attr, roff);
#ifdef STATX_BTIME
nt2kernel(std5->cr_time, &ni->i_crtime);
#endif
nt2kernel(std5->a_time, &ts);
inode_set_atime_to_ts(inode, ts);
nt2kernel(std5->c_time, &ts);
@@ -555,194 +554,148 @@ struct inode *ntfs_iget5(struct super_block *sb, const struct MFT_REF *ref,
return inode;
}
enum get_block_ctx {
GET_BLOCK_GENERAL = 0,
GET_BLOCK_WRITE_BEGIN = 1,
GET_BLOCK_DIRECT_IO_R = 2,
GET_BLOCK_DIRECT_IO_W = 3,
GET_BLOCK_BMAP = 4,
};
static noinline int ntfs_get_block_vbo(struct inode *inode, u64 vbo,
struct buffer_head *bh, int create,
enum get_block_ctx ctx)
{
struct super_block *sb = inode->i_sb;
struct ntfs_sb_info *sbi = sb->s_fs_info;
struct ntfs_inode *ni = ntfs_i(inode);
struct folio *folio = bh->b_folio;
u8 cluster_bits = sbi->cluster_bits;
u32 block_size = sb->s_blocksize;
u64 bytes, lbo, valid;
u32 off;
int err;
CLST vcn, lcn, len;
bool new;
/* Clear previous state. */
clear_buffer_new(bh);
clear_buffer_uptodate(bh);
if (is_resident(ni)) {
bh->b_blocknr = RESIDENT_LCN;
bh->b_size = block_size;
if (!folio) {
/* direct io (read) or bmap call */
err = 0;
} else {
ni_lock(ni);
err = attr_data_read_resident(ni, folio);
ni_unlock(ni);
if (!err)
set_buffer_uptodate(bh);
}
return err;
}
vcn = vbo >> cluster_bits;
off = vbo & sbi->cluster_mask;
new = false;
err = attr_data_get_block(ni, vcn, 1, &lcn, &len, create ? &new : NULL,
create && sbi->cluster_size > PAGE_SIZE);
if (err)
goto out;
if (!len)
return 0;
bytes = ((u64)len << cluster_bits) - off;
if (lcn >= sbi->used.bitmap.nbits) {
/* This case includes resident/compressed/sparse. */
if (!create) {
if (bh->b_size > bytes)
bh->b_size = bytes;
return 0;
}
WARN_ON(1);
}
if (new)
set_buffer_new(bh);
lbo = ((u64)lcn << cluster_bits) + off;
set_buffer_mapped(bh);
bh->b_bdev = sb->s_bdev;
bh->b_blocknr = lbo >> sb->s_blocksize_bits;
valid = ni->i_valid;
if (ctx == GET_BLOCK_DIRECT_IO_W) {
/* ntfs_direct_IO will update ni->i_valid. */
if (vbo >= valid)
set_buffer_new(bh);
} else if (create) {
/* Normal write. */
if (bytes > bh->b_size)
bytes = bh->b_size;
if (vbo >= valid)
set_buffer_new(bh);
if (vbo + bytes > valid) {
ni->i_valid = vbo + bytes;
mark_inode_dirty(inode);
}
} else if (vbo >= valid) {
/* Read out of valid data. */
clear_buffer_mapped(bh);
} else if (vbo + bytes <= valid) {
/* Normal read. */
} else if (vbo + block_size <= valid) {
/* Normal short read. */
bytes = block_size;
} else {
/*
* Read across valid size: vbo < valid && valid < vbo + block_size
*/
bytes = block_size;
if (folio) {
u32 voff = valid - vbo;
bh->b_size = block_size;
off = vbo & (PAGE_SIZE - 1);
folio_set_bh(bh, folio, off);
if (bh_read(bh, 0) < 0) {
err = -EIO;
goto out;
}
folio_zero_segment(folio, off + voff, off + block_size);
}
}
if (bh->b_size > bytes)
bh->b_size = bytes;
#ifndef __LP64__
if (ctx == GET_BLOCK_DIRECT_IO_W || ctx == GET_BLOCK_DIRECT_IO_R) {
static_assert(sizeof(size_t) < sizeof(loff_t));
if (bytes > 0x40000000u)
bh->b_size = 0x40000000u;
}
#endif
return 0;
out:
return err;
}
int ntfs_get_block(struct inode *inode, sector_t vbn,
struct buffer_head *bh_result, int create)
{
return ntfs_get_block_vbo(inode, (u64)vbn << inode->i_blkbits,
bh_result, create, GET_BLOCK_GENERAL);
}
static int ntfs_get_block_bmap(struct inode *inode, sector_t vsn,
struct buffer_head *bh_result, int create)
{
return ntfs_get_block_vbo(inode,
(u64)vsn << inode->i_sb->s_blocksize_bits,
bh_result, create, GET_BLOCK_BMAP);
}
static sector_t ntfs_bmap(struct address_space *mapping, sector_t block)
{
return generic_block_bmap(mapping, block, ntfs_get_block_bmap);
struct inode *inode = mapping->host;
struct ntfs_inode *ni = ntfs_i(inode);
/*
* We can get here for an inline file via the FIBMAP ioctl
*/
if (is_resident(ni))
return 0;
if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY) &&
!run_is_empty(&ni->file.run_da)) {
/*
* With delalloc data we want to sync the file so
* that we can make sure we allocate blocks for file and data
* is in place for the user to see it
*/
ni_allocate_da_blocks(ni);
}
return iomap_bmap(mapping, block, &ntfs_iomap_ops);
}
static void ntfs_iomap_read_end_io(struct bio *bio)
{
int error = blk_status_to_errno(bio->bi_status);
struct folio_iter fi;
bio_for_each_folio_all(fi, bio) {
struct folio *folio = fi.folio;
struct inode *inode = folio->mapping->host;
struct ntfs_inode *ni = ntfs_i(inode);
u64 valid = ni->i_valid;
u32 f_size = folio_size(folio);
loff_t f_pos = folio_pos(folio);
if (valid < f_pos + f_size) {
u32 z_from = valid <= f_pos ?
0 :
offset_in_folio(folio, valid);
/* The only thing ntfs_iomap_read_end_io used for. */
folio_zero_segment(folio, z_from, f_size);
}
iomap_finish_folio_read(folio, fi.offset, fi.length, error);
}
bio_put(bio);
}
/*
* Copied from iomap/bio.c.
*/
static int ntfs_iomap_bio_read_folio_range(const struct iomap_iter *iter,
struct iomap_read_folio_ctx *ctx,
size_t plen)
{
struct folio *folio = ctx->cur_folio;
const struct iomap *iomap = &iter->iomap;
loff_t pos = iter->pos;
size_t poff = offset_in_folio(folio, pos);
loff_t length = iomap_length(iter);
sector_t sector;
struct bio *bio = ctx->read_ctx;
sector = iomap_sector(iomap, pos);
if (!bio || bio_end_sector(bio) != sector ||
!bio_add_folio(bio, folio, plen, poff)) {
gfp_t gfp = mapping_gfp_constraint(folio->mapping, GFP_KERNEL);
gfp_t orig_gfp = gfp;
unsigned int nr_vecs = DIV_ROUND_UP(length, PAGE_SIZE);
if (bio)
submit_bio(bio);
if (ctx->rac) /* same as readahead_gfp_mask */
gfp |= __GFP_NORETRY | __GFP_NOWARN;
bio = bio_alloc(iomap->bdev, bio_max_segs(nr_vecs), REQ_OP_READ,
gfp);
/*
* If the bio_alloc fails, try it again for a single page to
* avoid having to deal with partial page reads. This emulates
* what do_mpage_read_folio does.
*/
if (!bio)
bio = bio_alloc(iomap->bdev, 1, REQ_OP_READ, orig_gfp);
if (ctx->rac)
bio->bi_opf |= REQ_RAHEAD;
bio->bi_iter.bi_sector = sector;
bio->bi_end_io = ntfs_iomap_read_end_io;
bio_add_folio_nofail(bio, folio, plen, poff);
ctx->read_ctx = bio;
}
return 0;
}
static void ntfs_iomap_bio_submit_read(struct iomap_read_folio_ctx *ctx)
{
struct bio *bio = ctx->read_ctx;
if (bio)
submit_bio(bio);
}
static const struct iomap_read_ops ntfs_iomap_bio_read_ops = {
.read_folio_range = ntfs_iomap_bio_read_folio_range,
.submit_read = ntfs_iomap_bio_submit_read,
};
static int ntfs_read_folio(struct file *file, struct folio *folio)
{
int err;
struct address_space *mapping = folio->mapping;
struct inode *inode = mapping->host;
struct ntfs_inode *ni = ntfs_i(inode);
loff_t vbo = folio_pos(folio);
struct iomap_read_folio_ctx ctx = {
.cur_folio = folio,
.ops = &ntfs_iomap_bio_read_ops,
};
if (is_resident(ni)) {
ni_lock(ni);
err = attr_data_read_resident(ni, folio);
ni_unlock(ni);
if (err != E_NTFS_NONRESIDENT) {
folio_unlock(folio);
return err;
}
if (unlikely(is_bad_ni(ni))) {
folio_unlock(folio);
return -EIO;
}
if (ni->i_valid <= vbo) {
folio_zero_range(folio, 0, folio_size(folio));
folio_mark_uptodate(folio);
folio_unlock(folio);
return 0;
}
if (is_compressed(ni)) {
ni_lock(ni);
err = ni_readpage_cmpr(ni, folio);
ni_unlock(ni);
/* ni_lock is taken inside ni_read_folio_cmpr after page locks */
err = ni_read_folio_cmpr(ni, folio);
return err;
}
/* Normal + sparse files. */
return mpage_read_folio(folio, ntfs_get_block);
iomap_read_folio(&ntfs_iomap_ops, &ctx, NULL);
return 0;
}
static void ntfs_readahead(struct readahead_control *rac)
@@ -750,8 +703,10 @@ static void ntfs_readahead(struct readahead_control *rac)
struct address_space *mapping = rac->mapping;
struct inode *inode = mapping->host;
struct ntfs_inode *ni = ntfs_i(inode);
u64 valid;
loff_t pos;
struct iomap_read_folio_ctx ctx = {
.ops = &ntfs_iomap_bio_read_ops,
.rac = rac,
};
if (is_resident(ni)) {
/* No readahead for resident. */
@@ -763,80 +718,7 @@ static void ntfs_readahead(struct readahead_control *rac)
return;
}
valid = ni->i_valid;
pos = readahead_pos(rac);
if (valid < i_size_read(inode) && pos <= valid &&
valid < pos + readahead_length(rac)) {
/* Range cross 'valid'. Read it page by page. */
return;
}
mpage_readahead(rac, ntfs_get_block);
}
static int ntfs_get_block_direct_IO_R(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create)
{
return ntfs_get_block_vbo(inode, (u64)iblock << inode->i_blkbits,
bh_result, create, GET_BLOCK_DIRECT_IO_R);
}
static int ntfs_get_block_direct_IO_W(struct inode *inode, sector_t iblock,
struct buffer_head *bh_result, int create)
{
return ntfs_get_block_vbo(inode, (u64)iblock << inode->i_blkbits,
bh_result, create, GET_BLOCK_DIRECT_IO_W);
}
static ssize_t ntfs_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *file = iocb->ki_filp;
struct address_space *mapping = file->f_mapping;
struct inode *inode = mapping->host;
struct ntfs_inode *ni = ntfs_i(inode);
loff_t vbo = iocb->ki_pos;
loff_t end;
int wr = iov_iter_rw(iter) & WRITE;
size_t iter_count = iov_iter_count(iter);
loff_t valid;
ssize_t ret;
if (is_resident(ni)) {
/* Switch to buffered write. */
ret = 0;
goto out;
}
if (is_compressed(ni)) {
ret = 0;
goto out;
}
ret = blockdev_direct_IO(iocb, inode, iter,
wr ? ntfs_get_block_direct_IO_W :
ntfs_get_block_direct_IO_R);
if (ret > 0)
end = vbo + ret;
else if (wr && ret == -EIOCBQUEUED)
end = vbo + iter_count;
else
goto out;
valid = ni->i_valid;
if (wr) {
if (end > valid && !S_ISBLK(inode->i_mode)) {
ni->i_valid = end;
mark_inode_dirty(inode);
}
} else if (vbo < valid && valid < end) {
/* Fix page. */
iov_iter_revert(iter, end - valid);
iov_iter_zero(end - valid, iter);
}
out:
return ret;
iomap_readahead(&ntfs_iomap_ops, &ctx, NULL);
}
int ntfs_set_size(struct inode *inode, u64 new_size)
@@ -849,29 +731,311 @@ int ntfs_set_size(struct inode *inode, u64 new_size)
/* Check for maximum file size. */
if (is_sparsed(ni) || is_compressed(ni)) {
if (new_size > sbi->maxbytes_sparse) {
err = -EFBIG;
goto out;
return -EFBIG;
}
} else if (new_size > sbi->maxbytes) {
err = -EFBIG;
goto out;
return -EFBIG;
}
ni_lock(ni);
down_write(&ni->file.run_lock);
err = attr_set_size(ni, ATTR_DATA, NULL, 0, &ni->file.run, new_size,
&ni->i_valid, true, NULL);
&ni->i_valid, true);
if (!err) {
i_size_write(inode, new_size);
mark_inode_dirty(inode);
}
up_write(&ni->file.run_lock);
ni_unlock(ni);
mark_inode_dirty(inode);
out:
return err;
}
/*
* Special value to detect ntfs_writeback_range call
*/
#define WB_NO_DA (struct iomap *)1
/*
* Function to get mapping vbo -> lbo.
* used with:
* - iomap_zero_range
* - iomap_truncate_page
* - iomap_dio_rw
* - iomap_file_buffered_write
* - iomap_bmap
* - iomap_fiemap
* - iomap_bio_read_folio
* - iomap_bio_readahead
*/
static int ntfs_iomap_begin(struct inode *inode, loff_t offset, loff_t length,
unsigned int flags, struct iomap *iomap,
struct iomap *srcmap)
{
struct ntfs_inode *ni = ntfs_i(inode);
struct ntfs_sb_info *sbi = ni->mi.sbi;
u8 cluster_bits = sbi->cluster_bits;
CLST vcn = offset >> cluster_bits;
u32 off = offset & sbi->cluster_mask;
bool rw = flags & IOMAP_WRITE;
loff_t endbyte = offset + length;
void *res = NULL;
int err;
CLST lcn, clen, clen_max = 1;
bool new_clst = false;
bool no_da;
bool zero = false;
if (unlikely(ntfs3_forced_shutdown(sbi->sb)))
return -EIO;
if (flags & IOMAP_REPORT) {
if (offset > ntfs_get_maxbytes(ni)) {
/* called from fiemap/bmap. */
return -EINVAL;
}
if (offset >= inode->i_size) {
/* special code for report. */
return -ENOENT;
}
}
if (IOMAP_ZERO == flags && (endbyte & sbi->cluster_mask)) {
rw = true;
} else if (rw) {
clen_max = bytes_to_cluster(sbi, endbyte) - vcn;
}
/*
* Force to allocate clusters if directIO(write) or writeback_range.
* NOTE: attr_data_get_block allocates clusters only for sparse file.
* Normal file allocates clusters in attr_set_size.
*/
no_da = flags == (IOMAP_DIRECT | IOMAP_WRITE) || srcmap == WB_NO_DA;
err = attr_data_get_block(ni, vcn, clen_max, &lcn, &clen,
rw ? &new_clst : NULL, zero, &res, no_da);
if (err) {
return err;
}
if (lcn == EOF_LCN) {
/* request out of file. */
if (flags & IOMAP_REPORT) {
/* special code for report. */
return -ENOENT;
}
if (rw) {
/* should never be here. */
return -EINVAL;
}
lcn = SPARSE_LCN;
}
iomap->flags = new_clst ? IOMAP_F_NEW : 0;
if (lcn == RESIDENT_LCN) {
if (offset >= clen) {
kfree(res);
if (flags & IOMAP_REPORT) {
/* special code for report. */
return -ENOENT;
}
return -EFAULT;
}
iomap->private = iomap->inline_data = res;
iomap->type = IOMAP_INLINE;
iomap->offset = 0;
iomap->length = clen; /* resident size in bytes. */
return 0;
}
if (!clen) {
/* broken file? */
return -EINVAL;
}
iomap->bdev = inode->i_sb->s_bdev;
iomap->offset = offset;
iomap->length = ((loff_t)clen << cluster_bits) - off;
if (lcn == COMPRESSED_LCN) {
/* should never be here. */
return -EOPNOTSUPP;
}
if (lcn == DELALLOC_LCN) {
iomap->type = IOMAP_DELALLOC;
iomap->addr = IOMAP_NULL_ADDR;
} else {
/* Translate clusters into bytes. */
iomap->addr = ((loff_t)lcn << cluster_bits) + off;
if (length && iomap->length > length)
iomap->length = length;
else
endbyte = offset + iomap->length;
if (lcn == SPARSE_LCN) {
iomap->addr = IOMAP_NULL_ADDR;
iomap->type = IOMAP_HOLE;
// if (IOMAP_ZERO == flags && !off) {
// iomap->length = (endbyte - offset) &
// sbi->cluster_mask_inv;
// }
} else if (endbyte <= ni->i_valid) {
iomap->type = IOMAP_MAPPED;
} else if (offset < ni->i_valid) {
iomap->type = IOMAP_MAPPED;
if (flags & IOMAP_REPORT)
iomap->length = ni->i_valid - offset;
} else if (rw || (flags & IOMAP_ZERO)) {
iomap->type = IOMAP_MAPPED;
} else {
iomap->type = IOMAP_UNWRITTEN;
}
}
if ((flags & IOMAP_ZERO) &&
(iomap->type == IOMAP_MAPPED || iomap->type == IOMAP_DELALLOC)) {
/* Avoid too large requests. */
u32 tail;
u32 off_a = offset & (PAGE_SIZE - 1);
if (off_a)
tail = PAGE_SIZE - off_a;
else
tail = PAGE_SIZE;
if (iomap->length > tail)
iomap->length = tail;
}
return 0;
}
static int ntfs_iomap_end(struct inode *inode, loff_t pos, loff_t length,
ssize_t written, unsigned int flags,
struct iomap *iomap)
{
int err = 0;
struct ntfs_inode *ni = ntfs_i(inode);
loff_t endbyte = pos + written;
if ((flags & IOMAP_WRITE) || (flags & IOMAP_ZERO)) {
if (iomap->type == IOMAP_INLINE) {
u32 data_size;
struct ATTRIB *attr;
struct mft_inode *mi;
attr = ni_find_attr(ni, NULL, NULL, ATTR_DATA, NULL, 0,
NULL, &mi);
if (!attr || attr->non_res) {
err = -EINVAL;
goto out;
}
data_size = le32_to_cpu(attr->res.data_size);
if (!(pos < data_size && endbyte <= data_size)) {
err = -EINVAL;
goto out;
}
/* Update resident data. */
memcpy(resident_data(attr) + pos,
iomap_inline_data(iomap, pos), written);
mi->dirty = true;
ni->i_valid = data_size;
} else if (ni->i_valid < endbyte) {
ni->i_valid = endbyte;
mark_inode_dirty(inode);
}
}
if ((flags & IOMAP_ZERO) &&
(iomap->type == IOMAP_MAPPED || iomap->type == IOMAP_DELALLOC)) {
/* Pair for code in ntfs_iomap_begin. */
balance_dirty_pages_ratelimited(inode->i_mapping);
cond_resched();
}
out:
if (iomap->type == IOMAP_INLINE) {
kfree(iomap->private);
iomap->private = NULL;
}
return err;
}
/*
* write_begin + put_folio + write_end.
* iomap_zero_range
* iomap_truncate_page
* iomap_file_buffered_write
*/
static void ntfs_iomap_put_folio(struct inode *inode, loff_t pos,
unsigned int len, struct folio *folio)
{
struct ntfs_inode *ni = ntfs_i(inode);
loff_t end = pos + len;
u32 f_size = folio_size(folio);
loff_t f_pos = folio_pos(folio);
loff_t f_end = f_pos + f_size;
if (ni->i_valid <= end && end < f_end) {
/* zero range [end - f_end). */
/* The only thing ntfs_iomap_put_folio used for. */
folio_zero_segment(folio, offset_in_folio(folio, end), f_size);
}
folio_unlock(folio);
folio_put(folio);
}
/*
* iomap_writeback_ops::writeback_range
*/
static ssize_t ntfs_writeback_range(struct iomap_writepage_ctx *wpc,
struct folio *folio, u64 offset,
unsigned int len, u64 end_pos)
{
struct iomap *iomap = &wpc->iomap;
/* Check iomap position. */
if (iomap->offset + iomap->length <= offset || offset < iomap->offset) {
int err;
struct inode *inode = wpc->inode;
struct ntfs_inode *ni = ntfs_i(inode);
struct ntfs_sb_info *sbi = ntfs_sb(inode->i_sb);
loff_t i_size_up = ntfs_up_cluster(sbi, inode->i_size);
loff_t len_max = i_size_up - offset;
err = ni->file.run_da.count ? ni_allocate_da_blocks(ni) : 0;
if (!err) {
/* Use local special value 'WB_NO_DA' to disable delalloc. */
err = ntfs_iomap_begin(inode, offset, len_max,
IOMAP_WRITE, iomap, WB_NO_DA);
}
if (err) {
ntfs_set_state(sbi, NTFS_DIRTY_DIRTY);
return err;
}
}
return iomap_add_to_ioend(wpc, folio, offset, end_pos, len);
}
static const struct iomap_writeback_ops ntfs_writeback_ops = {
.writeback_range = ntfs_writeback_range,
.writeback_submit = iomap_ioend_writeback_submit,
};
static int ntfs_resident_writepage(struct folio *folio,
struct writeback_control *wbc)
{
@@ -899,40 +1063,15 @@ static int ntfs_resident_writepage(struct folio *folio,
static int ntfs_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
struct inode *inode = mapping->host;
/* Avoid any operation if inode is bad. */
if (unlikely(is_bad_ni(ntfs_i(inode))))
return -EINVAL;
if (unlikely(ntfs3_forced_shutdown(inode->i_sb)))
return -EIO;
if (is_resident(ntfs_i(inode))) {
struct folio *folio = NULL;
int error;
while ((folio = writeback_iter(mapping, wbc, folio, &error)))
error = ntfs_resident_writepage(folio, wbc);
return error;
}
return mpage_writepages(mapping, wbc, ntfs_get_block);
}
static int ntfs_get_block_write_begin(struct inode *inode, sector_t vbn,
struct buffer_head *bh_result, int create)
{
return ntfs_get_block_vbo(inode, (u64)vbn << inode->i_blkbits,
bh_result, create, GET_BLOCK_WRITE_BEGIN);
}
int ntfs_write_begin(const struct kiocb *iocb, struct address_space *mapping,
loff_t pos, u32 len, struct folio **foliop, void **fsdata)
{
int err;
struct inode *inode = mapping->host;
struct ntfs_inode *ni = ntfs_i(inode);
struct iomap_writepage_ctx wpc = {
.inode = mapping->host,
.wbc = wbc,
.ops = &ntfs_writeback_ops,
};
/* Avoid any operation if inode is bad. */
if (unlikely(is_bad_ni(ni)))
@@ -942,100 +1081,15 @@ int ntfs_write_begin(const struct kiocb *iocb, struct address_space *mapping,
return -EIO;
if (is_resident(ni)) {
struct folio *folio = __filemap_get_folio(
mapping, pos >> PAGE_SHIFT, FGP_WRITEBEGIN,
mapping_gfp_mask(mapping));
struct folio *folio = NULL;
if (IS_ERR(folio)) {
err = PTR_ERR(folio);
goto out;
}
while ((folio = writeback_iter(mapping, wbc, folio, &err)))
err = ntfs_resident_writepage(folio, wbc);
ni_lock(ni);
err = attr_data_read_resident(ni, folio);
ni_unlock(ni);
if (!err) {
*foliop = folio;
goto out;
}
folio_unlock(folio);
folio_put(folio);
if (err != E_NTFS_NONRESIDENT)
goto out;
return err;
}
err = block_write_begin(mapping, pos, len, foliop,
ntfs_get_block_write_begin);
out:
return err;
}
/*
* ntfs_write_end - Address_space_operations::write_end.
*/
int ntfs_write_end(const struct kiocb *iocb, struct address_space *mapping,
loff_t pos, u32 len, u32 copied, struct folio *folio,
void *fsdata)
{
struct inode *inode = mapping->host;
struct ntfs_inode *ni = ntfs_i(inode);
u64 valid = ni->i_valid;
bool dirty = false;
int err;
if (is_resident(ni)) {
ni_lock(ni);
err = attr_data_write_resident(ni, folio);
ni_unlock(ni);
if (!err) {
struct buffer_head *head = folio_buffers(folio);
dirty = true;
/* Clear any buffers in folio. */
if (head) {
struct buffer_head *bh = head;
do {
clear_buffer_dirty(bh);
clear_buffer_mapped(bh);
set_buffer_uptodate(bh);
} while (head != (bh = bh->b_this_page));
}
folio_mark_uptodate(folio);
err = copied;
}
folio_unlock(folio);
folio_put(folio);
} else {
err = generic_write_end(iocb, mapping, pos, len, copied, folio,
fsdata);
}
if (err >= 0) {
if (!(ni->std_fa & FILE_ATTRIBUTE_ARCHIVE)) {
inode_set_mtime_to_ts(inode,
inode_set_ctime_current(inode));
ni->std_fa |= FILE_ATTRIBUTE_ARCHIVE;
dirty = true;
}
if (valid != ni->i_valid) {
/* ni->i_valid is changed in ntfs_get_block_vbo. */
dirty = true;
}
if (pos + err > inode->i_size) {
i_size_write(inode, pos + err);
dirty = true;
}
if (dirty)
mark_inode_dirty(inode);
}
return err;
return iomap_writepages(&wpc);
}
int ntfs3_write_inode(struct inode *inode, struct writeback_control *wbc)
@@ -1050,6 +1104,7 @@ int ntfs_sync_inode(struct inode *inode)
/*
* Helper function to read file.
* Used to read $AttrDef and $UpCase
*/
int inode_read_data(struct inode *inode, void *data, size_t bytes)
{
@@ -1539,9 +1594,10 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir,
attr->nres.alloc_size =
cpu_to_le64(ntfs_up_cluster(sbi, nsize));
err = attr_allocate_clusters(sbi, &ni->file.run, 0, 0,
clst, NULL, ALLOCATE_DEF,
&alen, 0, NULL, NULL);
err = attr_allocate_clusters(sbi, &ni->file.run, NULL,
0, 0, clst, NULL,
ALLOCATE_DEF, &alen, 0,
NULL, NULL);
if (err)
goto out5;
@@ -1682,7 +1738,7 @@ out6:
/* Delete ATTR_EA, if non-resident. */
struct runs_tree run;
run_init(&run);
attr_set_size(ni, ATTR_EA, NULL, 0, &run, 0, NULL, false, NULL);
attr_set_size(ni, ATTR_EA, NULL, 0, &run, 0, NULL, false);
run_close(&run);
}
@@ -2094,18 +2150,26 @@ const struct address_space_operations ntfs_aops = {
.read_folio = ntfs_read_folio,
.readahead = ntfs_readahead,
.writepages = ntfs_writepages,
.write_begin = ntfs_write_begin,
.write_end = ntfs_write_end,
.direct_IO = ntfs_direct_IO,
.bmap = ntfs_bmap,
.dirty_folio = block_dirty_folio,
.migrate_folio = buffer_migrate_folio,
.invalidate_folio = block_invalidate_folio,
.dirty_folio = iomap_dirty_folio,
.migrate_folio = filemap_migrate_folio,
.release_folio = iomap_release_folio,
.invalidate_folio = iomap_invalidate_folio,
};
const struct address_space_operations ntfs_aops_cmpr = {
.read_folio = ntfs_read_folio,
.dirty_folio = block_dirty_folio,
.direct_IO = ntfs_direct_IO,
.dirty_folio = iomap_dirty_folio,
.release_folio = iomap_release_folio,
.invalidate_folio = iomap_invalidate_folio,
};
const struct iomap_ops ntfs_iomap_ops = {
.iomap_begin = ntfs_iomap_begin,
.iomap_end = ntfs_iomap_end,
};
const struct iomap_write_ops ntfs_iomap_folio_ops = {
.put_folio = ntfs_iomap_put_folio,
};
// clang-format on
+4
View File
@@ -77,10 +77,14 @@ static_assert(sizeof(size_t) == 8);
typedef u32 CLST;
#endif
/* On-disk sparsed cluster is marked as -1. */
#define SPARSE_LCN64 ((u64)-1)
#define SPARSE_LCN ((CLST)-1)
/* Below is virtual (not on-disk) values. */
#define RESIDENT_LCN ((CLST)-2)
#define COMPRESSED_LCN ((CLST)-3)
#define EOF_LCN ((CLST)-4)
#define DELALLOC_LCN ((CLST)-5)
enum RECORD_NUM {
MFT_REC_MFT = 0,
+121 -32
View File
@@ -109,6 +109,7 @@ struct ntfs_mount_options {
unsigned force : 1; /* RW mount dirty volume. */
unsigned prealloc : 1; /* Preallocate space when file is growing. */
unsigned nocase : 1; /* case insensitive. */
unsigned delalloc : 1; /* delay allocation. */
};
/* Special value to unpack and deallocate. */
@@ -133,7 +134,8 @@ struct ntfs_buffers {
enum ALLOCATE_OPT {
ALLOCATE_DEF = 0, // Allocate all clusters.
ALLOCATE_MFT = 1, // Allocate for MFT.
ALLOCATE_ZERO = 2, // Zeroout new allocated clusters
ALLOCATE_ZERO = 2, // Zeroout new allocated clusters.
ALLOCATE_ONE_FR = 4, // Allocate one fragment only.
};
enum bitmap_mutex_classes {
@@ -192,6 +194,7 @@ struct ntfs_index {
struct runs_tree alloc_run;
/* read/write access to 'bitmap_run'/'alloc_run' while ntfs_readdir */
struct rw_semaphore run_lock;
size_t version; /* increment each change */
/*TODO: Remove 'cmp'. */
NTFS_CMP_FUNC cmp;
@@ -213,7 +216,7 @@ struct ntfs_sb_info {
u32 discard_granularity;
u64 discard_granularity_mask_inv; // ~(discard_granularity_mask_inv-1)
u32 bdev_blocksize_mask; // bdev_logical_block_size(bdev) - 1;
u32 bdev_blocksize; // bdev_logical_block_size(bdev)
u32 cluster_size; // bytes per cluster
u32 cluster_mask; // == cluster_size - 1
@@ -272,6 +275,12 @@ struct ntfs_sb_info {
struct {
struct wnd_bitmap bitmap; // $Bitmap::Data
CLST next_free_lcn;
/* Total sum of delay allocated clusters in all files. */
#ifdef CONFIG_NTFS3_64BIT_CLUSTER
atomic64_t da;
#else
atomic_t da;
#endif
} used;
struct {
@@ -379,7 +388,7 @@ struct ntfs_inode {
*/
u8 mi_loaded;
/*
/*
* Use this field to avoid any write(s).
* If inode is bad during initialization - use make_bad_inode
* If inode is bad during operations - use this field
@@ -390,7 +399,14 @@ struct ntfs_inode {
struct ntfs_index dir;
struct {
struct rw_semaphore run_lock;
/* Unpacked runs from just one record. */
struct runs_tree run;
/*
* Pairs [vcn, len] for all delay allocated clusters.
* Normal file always contains delayed clusters in one fragment.
* TODO: use 2 CLST per pair instead of 3.
*/
struct runs_tree run_da;
#ifdef CONFIG_NTFS3_LZX_XPRESS
struct folio *offs_folio;
#endif
@@ -430,20 +446,32 @@ enum REPARSE_SIGN {
/* Functions from attrib.c */
int attr_allocate_clusters(struct ntfs_sb_info *sbi, struct runs_tree *run,
CLST vcn, CLST lcn, CLST len, CLST *pre_alloc,
enum ALLOCATE_OPT opt, CLST *alen, const size_t fr,
CLST *new_lcn, CLST *new_len);
struct runs_tree *run_da, CLST vcn, CLST lcn,
CLST len, CLST *pre_alloc, enum ALLOCATE_OPT opt,
CLST *alen, const size_t fr, CLST *new_lcn,
CLST *new_len);
int attr_make_nonresident(struct ntfs_inode *ni, struct ATTRIB *attr,
struct ATTR_LIST_ENTRY *le, struct mft_inode *mi,
u64 new_size, struct runs_tree *run,
struct ATTRIB **ins_attr, struct page *page);
int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
const __le16 *name, u8 name_len, struct runs_tree *run,
u64 new_size, const u64 *new_valid, bool keep_prealloc,
struct ATTRIB **ret);
int attr_set_size_ex(struct ntfs_inode *ni, enum ATTR_TYPE type,
const __le16 *name, u8 name_len, struct runs_tree *run,
u64 new_size, const u64 *new_valid, bool keep_prealloc,
struct ATTRIB **ret, bool no_da);
static inline int attr_set_size(struct ntfs_inode *ni, enum ATTR_TYPE type,
const __le16 *name, u8 name_len,
struct runs_tree *run, u64 new_size,
const u64 *new_valid, bool keep_prealloc)
{
return attr_set_size_ex(ni, type, name, name_len, run, new_size,
new_valid, keep_prealloc, NULL, false);
}
int attr_data_get_block(struct ntfs_inode *ni, CLST vcn, CLST clen, CLST *lcn,
CLST *len, bool *new, bool zero);
int attr_data_read_resident(struct ntfs_inode *ni, struct folio *folio);
CLST *len, bool *new, bool zero, void **res,
bool no_da);
int attr_data_get_block_locked(struct ntfs_inode *ni, CLST vcn, CLST clen,
CLST *lcn, CLST *len, bool *new, bool zero,
void **res, bool no_da);
int attr_data_write_resident(struct ntfs_inode *ni, struct folio *folio);
int attr_load_runs_vcn(struct ntfs_inode *ni, enum ATTR_TYPE type,
const __le16 *name, u8 name_len, struct runs_tree *run,
@@ -512,6 +540,7 @@ int ntfs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
int ntfs_file_open(struct inode *inode, struct file *file);
int ntfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
__u64 start, __u64 len);
int ntfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync);
long ntfs_ioctl(struct file *filp, u32 cmd, unsigned long arg);
long ntfs_compat_ioctl(struct file *filp, u32 cmd, unsigned long arg);
extern const struct inode_operations ntfs_special_inode_operations;
@@ -567,9 +596,7 @@ enum REPARSE_SIGN ni_parse_reparse(struct ntfs_inode *ni, struct ATTRIB *attr,
struct REPARSE_DATA_BUFFER *buffer);
int ni_write_inode(struct inode *inode, int sync, const char *hint);
#define _ni_write_inode(i, w) ni_write_inode(i, w, __func__)
int ni_fiemap(struct ntfs_inode *ni, struct fiemap_extent_info *fieinfo,
__u64 vbo, __u64 len);
int ni_readpage_cmpr(struct ntfs_inode *ni, struct folio *folio);
int ni_read_folio_cmpr(struct ntfs_inode *ni, struct folio *folio);
int ni_decompress_file(struct ntfs_inode *ni);
int ni_read_frame(struct ntfs_inode *ni, u64 frame_vbo, struct page **pages,
u32 pages_per_frame, int copy);
@@ -590,6 +617,10 @@ int ni_rename(struct ntfs_inode *dir_ni, struct ntfs_inode *new_dir_ni,
struct NTFS_DE *new_de);
bool ni_is_dirty(struct inode *inode);
loff_t ni_seek_data_or_hole(struct ntfs_inode *ni, loff_t offset, bool data);
int ni_write_parents(struct ntfs_inode *ni, int sync);
int ni_allocate_da_blocks(struct ntfs_inode *ni);
int ni_allocate_da_blocks_locked(struct ntfs_inode *ni);
/* Globals from fslog.c */
bool check_index_header(const struct INDEX_HDR *hdr, size_t bytes);
@@ -605,13 +636,14 @@ int ntfs_loadlog_and_replay(struct ntfs_inode *ni, struct ntfs_sb_info *sbi);
int ntfs_look_for_free_space(struct ntfs_sb_info *sbi, CLST lcn, CLST len,
CLST *new_lcn, CLST *new_len,
enum ALLOCATE_OPT opt);
bool ntfs_check_for_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST mlen);
bool ntfs_check_free_space(struct ntfs_sb_info *sbi, CLST clen, CLST mlen,
bool da);
int ntfs_look_free_mft(struct ntfs_sb_info *sbi, CLST *rno, bool mft,
struct ntfs_inode *ni, struct mft_inode **mi);
void ntfs_mark_rec_free(struct ntfs_sb_info *sbi, CLST rno, bool is_mft);
int ntfs_clear_mft_tail(struct ntfs_sb_info *sbi, size_t from, size_t to);
int ntfs_refresh_zone(struct ntfs_sb_info *sbi);
void ntfs_update_mftmirr(struct ntfs_sb_info *sbi, int wait);
void ntfs_update_mftmirr(struct ntfs_sb_info *sbi);
void ntfs_bad_inode(struct inode *inode, const char *hint);
#define _ntfs_bad_inode(i) ntfs_bad_inode(i, __func__)
enum NTFS_DIRTY_FLAGS {
@@ -626,11 +658,27 @@ int ntfs_sb_write_run(struct ntfs_sb_info *sbi, const struct runs_tree *run,
u64 vbo, const void *buf, size_t bytes, int sync);
struct buffer_head *ntfs_bread_run(struct ntfs_sb_info *sbi,
const struct runs_tree *run, u64 vbo);
int ntfs_read_run_nb(struct ntfs_sb_info *sbi, const struct runs_tree *run,
u64 vbo, void *buf, u32 bytes, struct ntfs_buffers *nb);
int ntfs_read_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo,
struct NTFS_RECORD_HEADER *rhdr, u32 bytes,
struct ntfs_buffers *nb);
int ntfs_read_run_nb_ra(struct ntfs_sb_info *sbi, const struct runs_tree *run,
u64 vbo, void *buf, u32 bytes, struct ntfs_buffers *nb,
struct file_ra_state *ra);
static inline int ntfs_read_run_nb(struct ntfs_sb_info *sbi,
const struct runs_tree *run, u64 vbo,
void *buf, u32 bytes,
struct ntfs_buffers *nb)
{
return ntfs_read_run_nb_ra(sbi, run, vbo, buf, bytes, nb, NULL);
}
int ntfs_read_bh_ra(struct ntfs_sb_info *sbi, const struct runs_tree *run,
u64 vbo, struct NTFS_RECORD_HEADER *rhdr, u32 bytes,
struct ntfs_buffers *nb, struct file_ra_state *ra);
static inline int ntfs_read_bh(struct ntfs_sb_info *sbi,
const struct runs_tree *run, u64 vbo,
struct NTFS_RECORD_HEADER *rhdr, u32 bytes,
struct ntfs_buffers *nb)
{
return ntfs_read_bh_ra(sbi, run, vbo, rhdr, bytes, nb, NULL);
}
int ntfs_get_bh(struct ntfs_sb_info *sbi, const struct runs_tree *run, u64 vbo,
u32 bytes, struct ntfs_buffers *nb);
int ntfs_write_bh(struct ntfs_sb_info *sbi, struct NTFS_RECORD_HEADER *rhdr,
@@ -696,8 +744,13 @@ int indx_init(struct ntfs_index *indx, struct ntfs_sb_info *sbi,
const struct ATTRIB *attr, enum index_mutex_classed type);
struct INDEX_ROOT *indx_get_root(struct ntfs_index *indx, struct ntfs_inode *ni,
struct ATTRIB **attr, struct mft_inode **mi);
int indx_read(struct ntfs_index *idx, struct ntfs_inode *ni, CLST vbn,
struct indx_node **node);
int indx_read_ra(struct ntfs_index *idx, struct ntfs_inode *ni, CLST vbn,
struct indx_node **node, struct file_ra_state *ra);
static inline int indx_read(struct ntfs_index *idx, struct ntfs_inode *ni,
CLST vbn, struct indx_node **node)
{
return indx_read_ra(idx, ni, vbn, node, NULL);
}
int indx_find(struct ntfs_index *indx, struct ntfs_inode *dir,
const struct INDEX_ROOT *root, const void *Key, size_t KeyLen,
const void *param, int *diff, struct NTFS_DE **entry,
@@ -721,13 +774,6 @@ int indx_update_dup(struct ntfs_inode *ni, struct ntfs_sb_info *sbi,
struct inode *ntfs_iget5(struct super_block *sb, const struct MFT_REF *ref,
const struct cpu_str *name);
int ntfs_set_size(struct inode *inode, u64 new_size);
int ntfs_get_block(struct inode *inode, sector_t vbn,
struct buffer_head *bh_result, int create);
int ntfs_write_begin(const struct kiocb *iocb, struct address_space *mapping,
loff_t pos, u32 len, struct folio **foliop, void **fsdata);
int ntfs_write_end(const struct kiocb *iocb, struct address_space *mapping,
loff_t pos, u32 len, u32 copied, struct folio *folio,
void *fsdata);
int ntfs3_write_inode(struct inode *inode, struct writeback_control *wbc);
int ntfs_sync_inode(struct inode *inode);
int inode_read_data(struct inode *inode, void *data, size_t bytes);
@@ -738,6 +784,8 @@ int ntfs_create_inode(struct mnt_idmap *idmap, struct inode *dir,
int ntfs_link_inode(struct inode *inode, struct dentry *dentry);
int ntfs_unlink_inode(struct inode *dir, const struct dentry *dentry);
void ntfs_evict_inode(struct inode *inode);
extern const struct iomap_ops ntfs_iomap_ops;
extern const struct iomap_write_ops ntfs_iomap_folio_ops;
extern const struct inode_operations ntfs_link_inode_operations;
extern const struct address_space_operations ntfs_aops;
extern const struct address_space_operations ntfs_aops_cmpr;
@@ -815,7 +863,8 @@ void run_truncate_around(struct runs_tree *run, CLST vcn);
bool run_add_entry(struct runs_tree *run, CLST vcn, CLST lcn, CLST len,
bool is_mft);
bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len, CLST sub);
bool run_insert_range(struct runs_tree *run, CLST vcn, CLST len);
int run_insert_range(struct runs_tree *run, CLST vcn, CLST len);
int run_insert_range_da(struct runs_tree *run, CLST vcn, CLST len);
bool run_get_entry(const struct runs_tree *run, size_t index, CLST *vcn,
CLST *lcn, CLST *len);
bool run_is_mapped_full(const struct runs_tree *run, CLST svcn, CLST evcn);
@@ -835,6 +884,9 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
#endif
int run_get_highest_vcn(CLST vcn, const u8 *run_buf, u64 *highest_vcn);
int run_clone(const struct runs_tree *run, struct runs_tree *new_run);
bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done);
CLST run_len(const struct runs_tree *run);
CLST run_get_max_vcn(const struct runs_tree *run);
/* Globals from super.c */
void *ntfs_set_shared(void *ptr, u32 bytes);
@@ -1011,6 +1063,36 @@ static inline int ntfs3_forced_shutdown(struct super_block *sb)
return test_bit(NTFS_FLAGS_SHUTDOWN_BIT, &ntfs_sb(sb)->flags);
}
/* Returns total sum of delay allocated clusters in all files. */
static inline CLST ntfs_get_da(struct ntfs_sb_info *sbi)
{
#ifdef CONFIG_NTFS3_64BIT_CLUSTER
return atomic64_read(&sbi->used.da);
#else
return atomic_read(&sbi->used.da);
#endif
}
/* Update total count of delay allocated clusters. */
static inline void ntfs_add_da(struct ntfs_sb_info *sbi, CLST da)
{
#ifdef CONFIG_NTFS3_64BIT_CLUSTER
atomic64_add(da, &sbi->used.da);
#else
atomic_add(da, &sbi->used.da);
#endif
}
/* Update total count of delay allocated clusters. */
static inline void ntfs_sub_da(struct ntfs_sb_info *sbi, CLST da)
{
#ifdef CONFIG_NTFS3_64BIT_CLUSTER
atomic64_sub(da, &sbi->used.da);
#else
atomic_sub(da, &sbi->used.da);
#endif
}
/*
* ntfs_up_cluster - Align up on cluster boundary.
*/
@@ -1084,6 +1166,13 @@ static inline int is_resident(struct ntfs_inode *ni)
return ni->ni_flags & NI_FLAG_RESIDENT;
}
static inline loff_t ntfs_get_maxbytes(struct ntfs_inode *ni)
{
struct ntfs_sb_info *sbi = ni->mi.sbi;
return is_sparsed(ni) || is_compressed(ni) ? sbi->maxbytes_sparse :
sbi->maxbytes;
}
static inline void le16_sub_cpu(__le16 *var, u16 val)
{
*var = cpu_to_le16(le16_to_cpu(*var) - val);
+149 -14
View File
@@ -454,7 +454,7 @@ requires_new_range:
/*
* If existing range fits then were done.
* Otherwise extend found one and fall back to range jocode.
* Otherwise extend found one and fall back to range join code.
*/
if (r->vcn + r->len < vcn + len)
r->len += len - ((r->vcn + r->len) - vcn);
@@ -482,7 +482,8 @@ requires_new_range:
return true;
}
/* run_collapse_range
/*
* run_collapse_range
*
* Helper for attr_collapse_range(),
* which is helper for fallocate(collapse_range).
@@ -493,8 +494,9 @@ bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len, CLST sub)
struct ntfs_run *r, *e, *eat_start, *eat_end;
CLST end;
if (WARN_ON(!run_lookup(run, vcn, &index)))
return true; /* Should never be here. */
if (!run_lookup(run, vcn, &index) && index >= run->count) {
return true;
}
e = run->runs + run->count;
r = run->runs + index;
@@ -560,13 +562,13 @@ bool run_collapse_range(struct runs_tree *run, CLST vcn, CLST len, CLST sub)
* Helper for attr_insert_range(),
* which is helper for fallocate(insert_range).
*/
bool run_insert_range(struct runs_tree *run, CLST vcn, CLST len)
int run_insert_range(struct runs_tree *run, CLST vcn, CLST len)
{
size_t index;
struct ntfs_run *r, *e;
if (WARN_ON(!run_lookup(run, vcn, &index)))
return false; /* Should never be here. */
return -EINVAL; /* Should never be here. */
e = run->runs + run->count;
r = run->runs + index;
@@ -588,13 +590,49 @@ bool run_insert_range(struct runs_tree *run, CLST vcn, CLST len)
r->len = len1;
if (!run_add_entry(run, vcn + len, lcn2, len2, false))
return false;
return -ENOMEM;
}
if (!run_add_entry(run, vcn, SPARSE_LCN, len, false))
return false;
return -ENOMEM;
return true;
return 0;
}
/* run_insert_range_da
*
* Helper for attr_insert_range(),
* which is helper for fallocate(insert_range).
*/
int run_insert_range_da(struct runs_tree *run, CLST vcn, CLST len)
{
struct ntfs_run *r, *r0 = NULL, *e = run->runs + run->count;
;
for (r = run->runs; r < e; r++) {
CLST end = r->vcn + r->len;
if (vcn >= end)
continue;
if (!r0 && r->vcn < vcn) {
r0 = r;
} else {
r->vcn += len;
}
}
if (r0) {
/* split fragment. */
CLST len1 = vcn - r0->vcn;
CLST len2 = r0->len - len1;
r0->len = len1;
if (!run_add_entry(run, vcn + len, SPARSE_LCN, len2, false))
return -ENOMEM;
}
return 0;
}
/*
@@ -1131,11 +1169,14 @@ int run_unpack_ex(struct runs_tree *run, struct ntfs_sb_info *sbi, CLST ino,
struct rw_semaphore *lock =
is_mounted(sbi) ? &sbi->mft.ni->file.run_lock :
NULL;
if (lock)
down_read(lock);
ntfs_refresh_zone(sbi);
if (lock)
up_read(lock);
if (lock) {
if (down_read_trylock(lock)) {
ntfs_refresh_zone(sbi);
up_read(lock);
}
} else {
ntfs_refresh_zone(sbi);
}
}
up_write(&wnd->rw_lock);
if (err)
@@ -1206,3 +1247,97 @@ int run_clone(const struct runs_tree *run, struct runs_tree *new_run)
new_run->count = run->count;
return 0;
}
/*
* run_remove_range
*
*/
bool run_remove_range(struct runs_tree *run, CLST vcn, CLST len, CLST *done)
{
size_t index, eat;
struct ntfs_run *r, *e, *eat_start, *eat_end;
CLST end, d;
*done = 0;
/* Fast check. */
if (!run->count)
return true;
if (!run_lookup(run, vcn, &index) && index >= run->count) {
/* No entries in this run. */
return true;
}
e = run->runs + run->count;
r = run->runs + index;
end = vcn + len;
if (vcn > r->vcn) {
CLST r_end = r->vcn + r->len;
d = vcn - r->vcn;
if (r_end > end) {
/* Remove a middle part, split. */
*done += len;
r->len = d;
return run_add_entry(run, end, r->lcn, r_end - end,
false);
}
/* Remove tail of run .*/
*done += r->len - d;
r->len = d;
r += 1;
}
eat_start = r;
eat_end = r;
for (; r < e; r++) {
if (r->vcn >= end)
continue;
if (r->vcn + r->len <= end) {
/* Eat this run. */
*done += r->len;
eat_end = r + 1;
continue;
}
d = end - r->vcn;
*done += d;
if (r->lcn != SPARSE_LCN)
r->lcn += d;
r->len -= d;
r->vcn = end;
}
eat = eat_end - eat_start;
memmove(eat_start, eat_end, (e - eat_end) * sizeof(*r));
run->count -= eat;
return true;
}
CLST run_len(const struct runs_tree *run)
{
const struct ntfs_run *r, *e;
CLST len = 0;
for (r = run->runs, e = r + run->count; r < e; r++) {
len += r->len;
}
return len;
}
CLST run_get_max_vcn(const struct runs_tree *run)
{
const struct ntfs_run *r;
if (!run->count)
return 0;
r = run->runs + run->count - 1;
return r->vcn + r->len;
}
+50 -23
View File
@@ -58,9 +58,9 @@
#include <linux/buffer_head.h>
#include <linux/exportfs.h>
#include <linux/fs.h>
#include <linux/fs_struct.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/fs_struct.h>
#include <linux/log2.h>
#include <linux/minmax.h>
#include <linux/module.h>
@@ -264,9 +264,13 @@ enum Opt {
Opt_windows_names,
Opt_showmeta,
Opt_acl,
Opt_acl_bool,
Opt_iocharset,
Opt_prealloc,
Opt_prealloc_bool,
Opt_nocase,
Opt_delalloc,
Opt_delalloc_bool,
Opt_err,
};
@@ -285,10 +289,14 @@ static const struct fs_parameter_spec ntfs_fs_parameters[] = {
fsparam_flag("hide_dot_files", Opt_hide_dot_files),
fsparam_flag("windows_names", Opt_windows_names),
fsparam_flag("showmeta", Opt_showmeta),
fsparam_flag_no("acl", Opt_acl),
fsparam_flag("acl", Opt_acl),
fsparam_bool("acl", Opt_acl_bool),
fsparam_string("iocharset", Opt_iocharset),
fsparam_flag_no("prealloc", Opt_prealloc),
fsparam_flag("prealloc", Opt_prealloc),
fsparam_bool("prealloc", Opt_prealloc_bool),
fsparam_flag("nocase", Opt_nocase),
fsparam_flag("delalloc", Opt_delalloc),
fsparam_bool("delalloc", Opt_delalloc_bool),
{}
};
// clang-format on
@@ -379,15 +387,17 @@ static int ntfs_fs_parse_param(struct fs_context *fc,
case Opt_showmeta:
opts->showmeta = 1;
break;
case Opt_acl:
if (!result.negated)
case Opt_acl_bool:
if (result.boolean) {
fallthrough;
case Opt_acl:
#ifdef CONFIG_NTFS3_FS_POSIX_ACL
fc->sb_flags |= SB_POSIXACL;
#else
return invalf(
fc, "ntfs3: Support for ACL not compiled in!");
#endif
else
} else
fc->sb_flags &= ~SB_POSIXACL;
break;
case Opt_iocharset:
@@ -396,11 +406,20 @@ static int ntfs_fs_parse_param(struct fs_context *fc,
param->string = NULL;
break;
case Opt_prealloc:
opts->prealloc = !result.negated;
opts->prealloc = 1;
break;
case Opt_prealloc_bool:
opts->prealloc = result.boolean;
break;
case Opt_nocase:
opts->nocase = 1;
break;
case Opt_delalloc:
opts->delalloc = 1;
break;
case Opt_delalloc_bool:
opts->delalloc = result.boolean;
break;
default:
/* Should not be here unless we forget add case. */
return -EINVAL;
@@ -674,7 +693,7 @@ static noinline void ntfs3_put_sbi(struct ntfs_sb_info *sbi)
sbi->volume.ni = NULL;
}
ntfs_update_mftmirr(sbi, 0);
ntfs_update_mftmirr(sbi);
indx_clear(&sbi->security.index_sii);
indx_clear(&sbi->security.index_sdh);
@@ -705,9 +724,7 @@ static void ntfs_put_super(struct super_block *sb)
ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
if (sbi->options) {
unload_nls(sbi->options->nls);
kfree(sbi->options->nls_name);
kfree(sbi->options);
put_mount_options(sbi->options);
sbi->options = NULL;
}
@@ -719,14 +736,22 @@ static int ntfs_statfs(struct dentry *dentry, struct kstatfs *buf)
struct super_block *sb = dentry->d_sb;
struct ntfs_sb_info *sbi = sb->s_fs_info;
struct wnd_bitmap *wnd = &sbi->used.bitmap;
CLST da_clusters = ntfs_get_da(sbi);
buf->f_type = sb->s_magic;
buf->f_bsize = sbi->cluster_size;
buf->f_bsize = buf->f_frsize = sbi->cluster_size;
buf->f_blocks = wnd->nbits;
buf->f_bfree = buf->f_bavail = wnd_zeroes(wnd);
buf->f_bfree = wnd_zeroes(wnd);
if (buf->f_bfree > da_clusters) {
buf->f_bfree -= da_clusters;
} else {
buf->f_bfree = 0;
}
buf->f_bavail = buf->f_bfree;
buf->f_fsid.val[0] = sbi->volume.ser_num;
buf->f_fsid.val[1] = (sbi->volume.ser_num >> 32);
buf->f_fsid.val[1] = sbi->volume.ser_num >> 32;
buf->f_namelen = NTFS_NAME_LEN;
return 0;
@@ -771,6 +796,8 @@ static int ntfs_show_options(struct seq_file *m, struct dentry *root)
seq_puts(m, ",prealloc");
if (opts->nocase)
seq_puts(m, ",nocase");
if (opts->delalloc)
seq_puts(m, ",delalloc");
return 0;
}
@@ -823,7 +850,12 @@ static int ntfs_sync_fs(struct super_block *sb, int wait)
if (!err)
ntfs_set_state(sbi, NTFS_DIRTY_CLEAR);
ntfs_update_mftmirr(sbi, wait);
ntfs_update_mftmirr(sbi);
if (wait) {
sync_blockdev(sb->s_bdev);
blkdev_issue_flush(sb->s_bdev);
}
return err;
}
@@ -1076,7 +1108,7 @@ read_boot:
dev_size += sector_size - 1;
}
sbi->bdev_blocksize_mask = max(boot_sector_size, sector_size) - 1;
sbi->bdev_blocksize = max(boot_sector_size, sector_size);
sbi->mft.lbo = mlcn << cluster_bits;
sbi->mft.lbo2 = mlcn2 << cluster_bits;
@@ -1253,7 +1285,6 @@ static int ntfs_fill_super(struct super_block *sb, struct fs_context *fc)
}
}
sbi->options = options;
fc->fs_private = NULL;
sb->s_flags |= SB_NODIRATIME;
sb->s_magic = 0x7366746e; // "ntfs"
sb->s_op = &ntfs_sops;
@@ -1652,9 +1683,7 @@ load_root:
*/
struct buffer_head *bh0 = sb_getblk(sb, 0);
if (bh0) {
if (buffer_locked(bh0))
__wait_on_buffer(bh0);
wait_on_buffer(bh0);
lock_buffer(bh0);
memcpy(bh0->b_data, boot2, sizeof(*boot2));
set_buffer_uptodate(bh0);
@@ -1679,9 +1708,7 @@ put_inode_out:
out:
/* sbi->options == options */
if (options) {
unload_nls(options->nls);
kfree(options->nls_name);
kfree(options);
put_mount_options(sbi->options);
sbi->options = NULL;
}
+1 -1
View File
@@ -460,7 +460,7 @@ update_ea:
new_sz = size;
err = attr_set_size(ni, ATTR_EA, NULL, 0, &ea_run, new_sz, &new_sz,
false, NULL);
false);
if (err)
goto out;
+13 -8
View File
@@ -159,6 +159,18 @@ invalid:
goto out;
}
bool ovl_uuid_match(struct ovl_fs *ofs, const struct super_block *sb,
const uuid_t *uuid)
{
/*
* Make sure that the stored uuid matches the uuid of the lower
* layer where file handle will be decoded.
* In case of uuid=off option just make sure that stored uuid is null.
*/
return ovl_origin_uuid(ofs) ? uuid_equal(uuid, &sb->s_uuid) :
uuid_is_null(uuid);
}
struct dentry *ovl_decode_real_fh(struct ovl_fs *ofs, struct ovl_fh *fh,
struct vfsmount *mnt, bool connected)
{
@@ -168,14 +180,7 @@ struct dentry *ovl_decode_real_fh(struct ovl_fs *ofs, struct ovl_fh *fh,
if (!capable(CAP_DAC_READ_SEARCH))
return NULL;
/*
* Make sure that the stored uuid matches the uuid of the lower
* layer where file handle will be decoded.
* In case of uuid=off option just make sure that stored uuid is null.
*/
if (ovl_origin_uuid(ofs) ?
!uuid_equal(&fh->fb.uuid, &mnt->mnt_sb->s_uuid) :
!uuid_is_null(&fh->fb.uuid))
if (!ovl_uuid_match(ofs, mnt->mnt_sb, &fh->fb.uuid))
return NULL;
bytes = (fh->fb.len - offsetof(struct ovl_fb, fid));
+2
View File
@@ -710,6 +710,8 @@ static inline int ovl_check_fh_len(struct ovl_fh *fh, int fh_len)
return ovl_check_fb_len(&fh->fb, fh_len - OVL_FH_WIRE_OFFSET);
}
bool ovl_uuid_match(struct ovl_fs *ofs, const struct super_block *sb,
const uuid_t *uuid);
struct dentry *ovl_decode_real_fh(struct ovl_fs *ofs, struct ovl_fh *fh,
struct vfsmount *mnt, bool connected);
int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected,
+6 -9
View File
@@ -940,7 +940,7 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid)
* disable lower file handle decoding on all of them.
*/
if (ofs->fs[i].is_lower &&
uuid_equal(&ofs->fs[i].sb->s_uuid, uuid)) {
ovl_uuid_match(ofs, ofs->fs[i].sb, uuid)) {
ofs->fs[i].bad_uuid = true;
return false;
}
@@ -952,6 +952,7 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid)
static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
{
struct super_block *sb = path->mnt->mnt_sb;
const uuid_t *uuid = ovl_origin_uuid(ofs) ? &sb->s_uuid : &uuid_null;
unsigned int i;
dev_t dev;
int err;
@@ -963,7 +964,7 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
return i;
}
if (!ovl_lower_uuid_ok(ofs, &sb->s_uuid)) {
if (!ovl_lower_uuid_ok(ofs, uuid)) {
bad_uuid = true;
if (ofs->config.xino == OVL_XINO_AUTO) {
ofs->config.xino = OVL_XINO_OFF;
@@ -975,9 +976,8 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
warn = true;
}
if (warn) {
pr_warn("%s uuid detected in lower fs '%pd2', falling back to xino=%s,index=off,nfs_export=off.\n",
uuid_is_null(&sb->s_uuid) ? "null" :
"conflicting",
pr_warn("%s uuid in non-single lower fs '%pd2', falling back to xino=%s,index=off,nfs_export=off.\n",
uuid_is_null(uuid) ? "null" : "conflicting",
path->dentry, ovl_xino_mode(&ofs->config));
}
}
@@ -1469,10 +1469,7 @@ static int ovl_fill_super_creds(struct fs_context *fc, struct super_block *sb)
if (!ovl_upper_mnt(ofs))
sb->s_flags |= SB_RDONLY;
if (!ovl_origin_uuid(ofs) && ofs->numfs > 1) {
pr_warn("The uuid=off requires a single fs for lower and upper, falling back to uuid=null.\n");
ofs->config.uuid = OVL_UUID_NULL;
} else if (ovl_has_fsid(ofs) && ovl_upper_mnt(ofs)) {
if (ovl_has_fsid(ofs) && ovl_upper_mnt(ofs)) {
/* Use per instance persistent uuid/fsid */
ovl_init_uuid_xattr(sb, ofs, &ctx->upper);
}
+18 -4
View File
@@ -1481,10 +1481,24 @@ static struct file_system_type pipe_fs_type = {
};
#ifdef CONFIG_SYSCTL
static SYSCTL_USER_TO_KERN_UINT_CONV(_pipe_maxsz, round_pipe_size)
static SYSCTL_UINT_CONV_CUSTOM(_pipe_maxsz,
sysctl_user_to_kern_uint_conv_pipe_maxsz,
sysctl_kern_to_user_uint_conv, true)
static ulong round_pipe_size_ul(ulong size)
{
return round_pipe_size(size);
}
static int u2k_pipe_maxsz(const ulong *u_ptr, uint *k_ptr)
{
return proc_uint_u2k_conv_uop(u_ptr, k_ptr, round_pipe_size_ul);
}
static int do_proc_uint_conv_pipe_maxsz(ulong *u_ptr, uint *k_ptr,
int dir, const struct ctl_table *table)
{
return proc_uint_conv(u_ptr, k_ptr, dir, table, true,
u2k_pipe_maxsz,
proc_uint_k2u_conv);
}
static int proc_dopipe_max_size(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
+3 -2
View File
@@ -89,8 +89,9 @@ struct ceph_dir_layout {
} __attribute__ ((packed));
/* crypto algorithms */
#define CEPH_CRYPTO_NONE 0x0
#define CEPH_CRYPTO_AES 0x1
#define CEPH_CRYPTO_NONE 0x0
#define CEPH_CRYPTO_AES 0x1
#define CEPH_CRYPTO_AES256KRB5 0x2 /* AES256-CTS-HMAC384-192 */
#define CEPH_AES_IV "cephsageyudagreg"
-1
View File
@@ -3525,7 +3525,6 @@ ssize_t simple_attr_write(struct file *file, const char __user *buf,
ssize_t simple_attr_write_signed(struct file *file, const char __user *buf,
size_t len, loff_t *ppos);
struct ctl_table;
int __init list_bdev_fs_names(char *buf, size_t size);
#define __FMODE_EXEC ((__force int) FMODE_EXEC)
-2
View File
@@ -16,8 +16,6 @@
#include <linux/userfaultfd_k.h>
#include <linux/nodemask.h>
struct ctl_table;
struct user_struct;
struct mmu_gather;
struct node;
+1 -1
View File
@@ -152,7 +152,7 @@ struct rapl_if_priv {
union rapl_reg reg_unit;
union rapl_reg regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX];
int limits[RAPL_DOMAIN_MAX];
int (*read_raw)(int id, struct reg_action *ra, bool atomic);
int (*read_raw)(int id, struct reg_action *ra, bool pmu_ctx);
int (*write_raw)(int id, struct reg_action *ra);
void *defaults;
void *rpi;
-1
View File
@@ -78,7 +78,6 @@ extern void console_verbose(void);
/* strlen("ratelimit") + 1 */
#define DEVKMSG_STR_MAX_SIZE 10
extern char devkmsg_log_str[DEVKMSG_STR_MAX_SIZE];
struct ctl_table;
extern int suppress_printk;
+17 -103
View File
@@ -59,7 +59,6 @@ extern const int sysctl_vals[];
#define SYSCTL_LONG_ONE ((void *)&sysctl_long_vals[1])
#define SYSCTL_LONG_MAX ((void *)&sysctl_long_vals[2])
#define SYSCTL_CONV_IDENTITY(val) (val)
/**
*
* "dir" originates from read_iter (dir = 0) or write_iter (dir = 1)
@@ -73,107 +72,6 @@ extern const int sysctl_vals[];
#define SYSCTL_USER_TO_KERN(dir) (!!(dir))
#define SYSCTL_KERN_TO_USER(dir) (!dir)
#define SYSCTL_USER_TO_KERN_INT_CONV(name, u_ptr_op) \
int sysctl_user_to_kern_int_conv##name(const bool *negp, \
const unsigned long *u_ptr,\
int *k_ptr) \
{ \
unsigned long u = u_ptr_op(*u_ptr); \
if (*negp) { \
if (u > (unsigned long) INT_MAX + 1) \
return -EINVAL; \
WRITE_ONCE(*k_ptr, -u); \
} else { \
if (u > (unsigned long) INT_MAX) \
return -EINVAL; \
WRITE_ONCE(*k_ptr, u); \
} \
return 0; \
}
#define SYSCTL_KERN_TO_USER_INT_CONV(name, k_ptr_op) \
int sysctl_kern_to_user_int_conv##name(bool *negp, \
unsigned long *u_ptr, \
const int *k_ptr) \
{ \
int val = READ_ONCE(*k_ptr); \
if (val < 0) { \
*negp = true; \
*u_ptr = -k_ptr_op((unsigned long)val); \
} else { \
*negp = false; \
*u_ptr = k_ptr_op((unsigned long)val); \
} \
return 0; \
}
/**
* To range check on a converted value, use a temp k_ptr
* When checking range, value should be within (tbl->extra1, tbl->extra2)
*/
#define SYSCTL_INT_CONV_CUSTOM(name, user_to_kern, kern_to_user, \
k_ptr_range_check) \
int do_proc_int_conv##name(bool *negp, unsigned long *u_ptr, int *k_ptr,\
int dir, const struct ctl_table *tbl) \
{ \
if (SYSCTL_KERN_TO_USER(dir)) \
return kern_to_user(negp, u_ptr, k_ptr); \
\
if (k_ptr_range_check) { \
int tmp_k, ret; \
if (!tbl) \
return -EINVAL; \
ret = user_to_kern(negp, u_ptr, &tmp_k); \
if (ret) \
return ret; \
if ((tbl->extra1 && *(int *)tbl->extra1 > tmp_k) || \
(tbl->extra2 && *(int *)tbl->extra2 < tmp_k)) \
return -EINVAL; \
WRITE_ONCE(*k_ptr, tmp_k); \
} else \
return user_to_kern(negp, u_ptr, k_ptr); \
return 0; \
}
#define SYSCTL_USER_TO_KERN_UINT_CONV(name, u_ptr_op) \
int sysctl_user_to_kern_uint_conv##name(const unsigned long *u_ptr,\
unsigned int *k_ptr) \
{ \
unsigned long u = u_ptr_op(*u_ptr); \
if (u > UINT_MAX) \
return -EINVAL; \
WRITE_ONCE(*k_ptr, u); \
return 0; \
}
#define SYSCTL_UINT_CONV_CUSTOM(name, user_to_kern, kern_to_user, \
k_ptr_range_check) \
int do_proc_uint_conv##name(unsigned long *u_ptr, unsigned int *k_ptr, \
int dir, const struct ctl_table *tbl) \
{ \
if (SYSCTL_KERN_TO_USER(dir)) \
return kern_to_user(u_ptr, k_ptr); \
\
if (k_ptr_range_check) { \
unsigned int tmp_k; \
int ret; \
if (!tbl) \
return -EINVAL; \
ret = user_to_kern(u_ptr, &tmp_k); \
if (ret) \
return ret; \
if ((tbl->extra1 && \
*(unsigned int *)tbl->extra1 > tmp_k) || \
(tbl->extra2 && \
*(unsigned int *)tbl->extra2 < tmp_k)) \
return -ERANGE; \
WRITE_ONCE(*k_ptr, tmp_k); \
} else \
return user_to_kern(u_ptr, k_ptr); \
return 0; \
}
extern const unsigned long sysctl_long_vals[];
typedef int proc_handler(const struct ctl_table *ctl, int write, void *buffer,
@@ -182,6 +80,7 @@ typedef int proc_handler(const struct ctl_table *ctl, int write, void *buffer,
int proc_dostring(const struct ctl_table *, int, void *, size_t *, loff_t *);
int proc_dobool(const struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos);
int proc_dointvec(const struct ctl_table *, int, void *, size_t *, loff_t *);
int proc_dointvec_minmax(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos);
@@ -189,6 +88,15 @@ int proc_dointvec_conv(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr,
int dir, const struct ctl_table *table));
int proc_int_k2u_conv_kop(ulong *u_ptr, const int *k_ptr, bool *negp,
ulong (*k_ptr_op)(const ulong));
int proc_int_u2k_conv_uop(const ulong *u_ptr, int *k_ptr, const bool *negp,
ulong (*u_ptr_op)(const ulong));
int proc_int_conv(bool *negp, ulong *u_ptr, int *k_ptr, int dir,
const struct ctl_table *tbl, bool k_ptr_range_check,
int (*user_to_kern)(const bool *negp, const ulong *u_ptr, int *k_ptr),
int (*kern_to_user)(bool *negp, ulong *u_ptr, const int *k_ptr));
int proc_douintvec(const struct ctl_table *, int, void *, size_t *, loff_t *);
int proc_douintvec_minmax(const struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos);
@@ -196,6 +104,13 @@ int proc_douintvec_conv(const struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *lvalp, unsigned int *valp,
int write, const struct ctl_table *table));
int proc_uint_k2u_conv(ulong *u_ptr, const uint *k_ptr);
int proc_uint_u2k_conv_uop(const ulong *u_ptr, uint *k_ptr,
ulong (*u_ptr_op)(const ulong));
int proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir,
const struct ctl_table *tbl, bool k_ptr_range_check,
int (*user_to_kern)(const ulong *u_ptr, uint *k_ptr),
int (*kern_to_user)(ulong *u_ptr, const uint *k_ptr));
int proc_dou8vec_minmax(const struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos);
@@ -206,7 +121,6 @@ int proc_doulongvec_minmax_conv(const struct ctl_table *table, int dir,
int proc_do_large_bitmap(const struct ctl_table *, int, void *, size_t *, loff_t *);
int proc_do_static_key(const struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos);
int sysctl_kern_to_user_uint_conv(unsigned long *u_ptr, const unsigned int *k_ptr);
/*
* Register a set of sysctl names by calling register_sysctl
-2
View File
@@ -211,8 +211,6 @@ typedef struct {
unsigned short slave_timeout; /* when? */
} ax25_dama_info;
struct ctl_table;
typedef struct ax25_dev {
struct list_head list;
+1 -1
View File
@@ -4,9 +4,9 @@
*/
#include <linux/console.h>
#include <linux/types.h>
#include <linux/sysctl.h>
#if defined(CONFIG_PRINTK) && defined(CONFIG_SYSCTL)
struct ctl_table;
void __init printk_sysctl_init(void);
int devkmsg_sysctl_set_loglvl(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos);
-1
View File
@@ -3,7 +3,6 @@
* sysctl.c: General linux system control interface
*/
#include <linux/sysctl.h>
#include <linux/printk.h>
#include <linux/capability.h>
#include <linux/ratelimit.h>
+274 -24
View File
@@ -354,29 +354,221 @@ static void proc_put_char(void **buf, size_t *size, char c)
}
}
static SYSCTL_USER_TO_KERN_INT_CONV(, SYSCTL_CONV_IDENTITY)
static SYSCTL_KERN_TO_USER_INT_CONV(, SYSCTL_CONV_IDENTITY)
static SYSCTL_INT_CONV_CUSTOM(, sysctl_user_to_kern_int_conv,
sysctl_kern_to_user_int_conv, false)
static SYSCTL_INT_CONV_CUSTOM(_minmax, sysctl_user_to_kern_int_conv,
sysctl_kern_to_user_int_conv, true)
static SYSCTL_USER_TO_KERN_UINT_CONV(, SYSCTL_CONV_IDENTITY)
int sysctl_kern_to_user_uint_conv(unsigned long *u_ptr,
const unsigned int *k_ptr)
/**
* proc_uint_u2k_conv_uop - Assign user value to a kernel pointer
*
* @u_ptr: pointer to user space variable
* @k_ptr: pointer to kernel variable
* @u_ptr_op: execute this function before assigning to k_ptr
*
* Uses WRITE_ONCE to assign value to k_ptr. Executes u_ptr_op if
* not NULL. Check that the values are less than UINT_MAX to avoid
* having to support wrap around from userspace.
*
* returns 0 on success.
*/
int proc_uint_u2k_conv_uop(const ulong *u_ptr, uint *k_ptr,
ulong (*u_ptr_op)(const ulong))
{
unsigned int val = READ_ONCE(*k_ptr);
*u_ptr = (unsigned long)val;
ulong u = u_ptr_op ? u_ptr_op(*u_ptr) : *u_ptr;
if (u > UINT_MAX)
return -EINVAL;
WRITE_ONCE(*k_ptr, u);
return 0;
}
static SYSCTL_UINT_CONV_CUSTOM(, sysctl_user_to_kern_uint_conv,
sysctl_kern_to_user_uint_conv, false)
static SYSCTL_UINT_CONV_CUSTOM(_minmax, sysctl_user_to_kern_uint_conv,
sysctl_kern_to_user_uint_conv, true)
/**
* proc_uint_k2u_conv - Assign kernel value to a user space pointer
*
* @u_ptr: pointer to user space variable
* @k_ptr: pointer to kernel variable
*
* Uses READ_ONCE to assign value to u_ptr.
*
* returns 0 on success.
*/
int proc_uint_k2u_conv(ulong *u_ptr, const uint *k_ptr)
{
uint val = READ_ONCE(*k_ptr);
*u_ptr = (ulong)val;
return 0;
}
/**
* proc_uint_conv - Change user or kernel pointer based on direction
*
* @u_ptr: pointer to user variable
* @k_ptr: pointer to kernel variable
* @dir: %TRUE if this is a write to the sysctl file
* @tbl: the sysctl table
* @k_ptr_range_check: Check range for k_ptr when %TRUE
* @user_to_kern: Callback used to assign value from user to kernel var
* @kern_to_user: Callback used to assign value from kernel to user var
*
* When direction is kernel to user, then the u_ptr is modified.
* When direction is user to kernel, then the k_ptr is modified.
*
* Returns 0 on success
*/
int proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir,
const struct ctl_table *tbl, bool k_ptr_range_check,
int (*user_to_kern)(const ulong *u_ptr, uint *k_ptr),
int (*kern_to_user)(ulong *u_ptr, const uint *k_ptr))
{
if (SYSCTL_KERN_TO_USER(dir))
return kern_to_user(u_ptr, k_ptr);
if (k_ptr_range_check) {
uint tmp_k;
int ret;
if (!tbl)
return -EINVAL;
ret = user_to_kern(u_ptr, &tmp_k);
if (ret)
return ret;
if ((tbl->extra1 &&
*(uint *)tbl->extra1 > tmp_k) ||
(tbl->extra2 &&
*(uint *)tbl->extra2 < tmp_k))
return -ERANGE;
WRITE_ONCE(*k_ptr, tmp_k);
} else
return user_to_kern(u_ptr, k_ptr);
return 0;
}
static int proc_uint_u2k_conv(const ulong *u_ptr, uint *k_ptr)
{
return proc_uint_u2k_conv_uop(u_ptr, k_ptr, NULL);
}
static int do_proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir,
const struct ctl_table *tbl)
{
return proc_uint_conv(u_ptr, k_ptr, dir, tbl, false,
proc_uint_u2k_conv, proc_uint_k2u_conv);
}
static int do_proc_uint_conv_minmax(ulong *u_ptr, uint *k_ptr, int dir,
const struct ctl_table *tbl)
{
return proc_uint_conv(u_ptr, k_ptr, dir, tbl, true,
proc_uint_u2k_conv, proc_uint_k2u_conv);
}
/**
* proc_int_k2u_conv_kop - Assign kernel value to a user space pointer
* @u_ptr: pointer to user space variable
* @k_ptr: pointer to kernel variable
* @negp: assigned %TRUE if the converted kernel value is negative;
* %FALSE otherweise
* @k_ptr_op: execute this function before assigning to u_ptr
*
* Uses READ_ONCE to get value from k_ptr. Executes k_ptr_op before assigning
* to u_ptr if not NULL. Does **not** check for overflow.
*
* Returns: 0 on success.
*/
int proc_int_k2u_conv_kop(ulong *u_ptr, const int *k_ptr, bool *negp,
ulong (*k_ptr_op)(const ulong))
{
int val = READ_ONCE(*k_ptr);
if (val < 0) {
*negp = true;
*u_ptr = k_ptr_op ? -k_ptr_op((ulong)val) : -(ulong)val;
} else {
*negp = false;
*u_ptr = k_ptr_op ? k_ptr_op((ulong)val) : (ulong) val;
}
return 0;
}
/**
* proc_int_u2k_conv_uop - Assign user value to a kernel pointer
* @u_ptr: pointer to user space variable
* @k_ptr: pointer to kernel variable
* @negp: If %TRUE, the converted user value is made negative.
* @u_ptr_op: execute this function before assigning to k_ptr
*
* Uses WRITE_ONCE to assign value to k_ptr. Executes u_ptr_op if
* not NULL. Check for overflow with UINT_MAX.
*
* Returns: 0 on success.
*/
int proc_int_u2k_conv_uop(const ulong *u_ptr, int *k_ptr, const bool *negp,
ulong (*u_ptr_op)(const ulong))
{
ulong u = u_ptr_op ? u_ptr_op(*u_ptr) : *u_ptr;
if (*negp) {
if (u > (ulong) INT_MAX + 1)
return -EINVAL;
WRITE_ONCE(*k_ptr, -u);
} else {
if (u > (ulong) INT_MAX)
return -EINVAL;
WRITE_ONCE(*k_ptr, u);
}
return 0;
}
int proc_int_conv(bool *negp, ulong *u_ptr, int *k_ptr, int dir,
const struct ctl_table *tbl, bool k_ptr_range_check,
int (*user_to_kern)(const bool *negp, const ulong *u_ptr, int *k_ptr),
int (*kern_to_user)(bool *negp, ulong *u_ptr, const int *k_ptr))
{
if (SYSCTL_KERN_TO_USER(dir))
return kern_to_user(negp, u_ptr, k_ptr);
if (k_ptr_range_check) {
int tmp_k, ret;
if (!tbl)
return -EINVAL;
ret = user_to_kern(negp, u_ptr, &tmp_k);
if (ret)
return ret;
if ((tbl->extra1 && *(int *)tbl->extra1 > tmp_k) ||
(tbl->extra2 && *(int *)tbl->extra2 < tmp_k))
return -EINVAL;
WRITE_ONCE(*k_ptr, tmp_k);
} else
return user_to_kern(negp, u_ptr, k_ptr);
return 0;
}
static int sysctl_user_to_kern_int_conv(const bool *negp, const ulong *u_ptr,
int *k_ptr)
{
return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, NULL);
}
static int sysctl_kern_to_user_int_conv(bool *negp, ulong *u_ptr, const int *k_ptr)
{
return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, NULL);
}
static int do_proc_int_conv(bool *negp, unsigned long *u_ptr, int *k_ptr,
int dir, const struct ctl_table *tbl)
{
return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
sysctl_user_to_kern_int_conv,
sysctl_kern_to_user_int_conv);
}
static int do_proc_int_conv_minmax(bool *negp, unsigned long *u_ptr, int *k_ptr,
int dir, const struct ctl_table *tbl)
{
return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, true,
sysctl_user_to_kern_int_conv,
sysctl_kern_to_user_int_conv);
}
static const char proc_wspace_sep[] = { ' ', '\t', '\n' };
@@ -568,6 +760,22 @@ static int do_proc_douintvec(const struct ctl_table *table, int dir,
return do_proc_douintvec_r(table, buffer, lenp, ppos, conv);
}
/**
* proc_douintvec_conv - read a vector of unsigned ints with a custom converter
*
* @table: the sysctl table
* @dir: %TRUE if this is a write to the sysctl file
* @buffer: the user buffer
* @lenp: the size of the user buffer
* @ppos: file position
* @conv: Custom converter call back
*
* Reads/writes up to table->maxlen/sizeof(unsigned int) unsigned integer
* values from/to the user buffer, treated as an ASCII string. Negative
* strings are not allowed.
*
* Returns 0 on success
*/
int proc_douintvec_conv(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *u_ptr, unsigned int *k_ptr,
@@ -576,7 +784,6 @@ int proc_douintvec_conv(const struct ctl_table *table, int dir, void *buffer,
return do_proc_douintvec(table, dir, buffer, lenp, ppos, conv);
}
/**
* proc_dobool - read/write a bool
* @table: the sysctl table
@@ -692,10 +899,10 @@ int proc_dointvec_minmax(const struct ctl_table *table, int dir,
* values from/to the user buffer, treated as an ASCII string. Negative
* strings are not allowed.
*
* This routine will ensure the values are within the range specified by
* table->extra1 (min) and table->extra2 (max). There is a final sanity
* check for UINT_MAX to avoid having to support wrap around uses from
* userspace.
* When changing the kernel variable, this routine will ensure the values
* are within the range specified by table->extra1 (min) and table->extra2
* (max). And Check that the values are less than UINT_MAX to avoid having to
* support wrap around uses from userspace.
*
* Returns 0 on success or -ERANGE when range check failes and
* SYSCTL_USER_TO_KERN(dir) == true
@@ -862,6 +1069,22 @@ int proc_doulongvec_minmax(const struct ctl_table *table, int dir,
return proc_doulongvec_minmax_conv(table, dir, buffer, lenp, ppos, 1l, 1l);
}
/**
* proc_dointvec_conv - read a vector of ints with a custom converter
* @table: the sysctl table
* @dir: %TRUE if this is a write to the sysctl file
* @buffer: the user buffer
* @lenp: the size of the user buffer
* @ppos: file position
* @conv: Custom converter call back
*
* Reads/writes up to table->maxlen/sizeof(unsigned int) unsigned integer
* values from/to the user buffer, treated as an ASCII string. Negative
* strings are not allowed.
*
* Returns: 0 on success
*/
int proc_dointvec_conv(const struct ctl_table *table, int dir, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr,
@@ -1055,6 +1278,33 @@ int proc_douintvec_minmax(const struct ctl_table *table, int dir,
return -ENOSYS;
}
int proc_douintvec_conv(const struct ctl_table *table, int write, void *buffer,
size_t *lenp, loff_t *ppos,
int (*conv)(unsigned long *lvalp, unsigned int *valp,
int write, const struct ctl_table *table))
{
return -ENOSYS;
}
int proc_uint_k2u_conv(ulong *u_ptr, const uint *k_ptr)
{
return -ENOSYS;
}
int proc_uint_u2k_conv_uop(const ulong *u_ptr, uint *k_ptr,
ulong (*u_ptr_op)(const ulong))
{
return -ENOSYS;
}
int proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir,
const struct ctl_table *tbl, bool k_ptr_range_check,
int (*user_to_kern)(const ulong *u_ptr, uint *k_ptr),
int (*kern_to_user)(ulong *u_ptr, const uint *k_ptr))
{
return -ENOSYS;
}
int proc_dou8vec_minmax(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
+112 -18
View File
@@ -100,26 +100,120 @@ void __init register_refined_jiffies(long cycles_per_second)
__clocksource_register(&refined_jiffies);
}
#define SYSCTL_CONV_MULT_HZ(val) ((val) * HZ)
#define SYSCTL_CONV_DIV_HZ(val) ((val) / HZ)
#ifdef CONFIG_PROC_SYSCTL
static ulong mult_hz(const ulong val)
{
return val * HZ;
}
static SYSCTL_USER_TO_KERN_INT_CONV(_hz, SYSCTL_CONV_MULT_HZ)
static SYSCTL_KERN_TO_USER_INT_CONV(_hz, SYSCTL_CONV_DIV_HZ)
static SYSCTL_USER_TO_KERN_INT_CONV(_userhz, clock_t_to_jiffies)
static SYSCTL_KERN_TO_USER_INT_CONV(_userhz, jiffies_to_clock_t)
static SYSCTL_USER_TO_KERN_INT_CONV(_ms, msecs_to_jiffies)
static SYSCTL_KERN_TO_USER_INT_CONV(_ms, jiffies_to_msecs)
static ulong div_hz(const ulong val)
{
return val / HZ;
}
static SYSCTL_INT_CONV_CUSTOM(_jiffies, sysctl_user_to_kern_int_conv_hz,
sysctl_kern_to_user_int_conv_hz, false)
static SYSCTL_INT_CONV_CUSTOM(_userhz_jiffies,
sysctl_user_to_kern_int_conv_userhz,
sysctl_kern_to_user_int_conv_userhz, false)
static SYSCTL_INT_CONV_CUSTOM(_ms_jiffies, sysctl_user_to_kern_int_conv_ms,
sysctl_kern_to_user_int_conv_ms, false)
static SYSCTL_INT_CONV_CUSTOM(_ms_jiffies_minmax,
sysctl_user_to_kern_int_conv_ms,
sysctl_kern_to_user_int_conv_ms, true)
static int sysctl_u2k_int_conv_hz(const bool *negp, const ulong *u_ptr, int *k_ptr)
{
return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, mult_hz);
}
static int sysctl_k2u_int_conv_hz(bool *negp, ulong *u_ptr, const int *k_ptr)
{
return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, div_hz);
}
static int sysctl_u2k_int_conv_userhz(const bool *negp, const ulong *u_ptr, int *k_ptr)
{
return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, clock_t_to_jiffies);
}
static ulong sysctl_jiffies_to_clock_t(const ulong val)
{
return jiffies_to_clock_t(val);
}
static int sysctl_k2u_int_conv_userhz(bool *negp, ulong *u_ptr, const int *k_ptr)
{
return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, sysctl_jiffies_to_clock_t);
}
static ulong sysctl_msecs_to_jiffies(const ulong val)
{
return msecs_to_jiffies(val);
}
static int sysctl_u2k_int_conv_ms(const bool *negp, const ulong *u_ptr, int *k_ptr)
{
return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, sysctl_msecs_to_jiffies);
}
static ulong sysctl_jiffies_to_msecs(const ulong val)
{
return jiffies_to_msecs(val);
}
static int sysctl_k2u_int_conv_ms(bool *negp, ulong *u_ptr, const int *k_ptr)
{
return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, sysctl_jiffies_to_msecs);
}
static int do_proc_int_conv_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
int dir, const struct ctl_table *tbl)
{
return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
sysctl_u2k_int_conv_hz, sysctl_k2u_int_conv_hz);
}
static int do_proc_int_conv_userhz_jiffies(bool *negp, ulong *u_ptr,
int *k_ptr, int dir,
const struct ctl_table *tbl)
{
return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
sysctl_u2k_int_conv_userhz,
sysctl_k2u_int_conv_userhz);
}
static int do_proc_int_conv_ms_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
int dir, const struct ctl_table *tbl)
{
return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
sysctl_u2k_int_conv_ms, sysctl_k2u_int_conv_ms);
}
static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr,
int *k_ptr, int dir,
const struct ctl_table *tbl)
{
return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false,
sysctl_u2k_int_conv_ms, sysctl_k2u_int_conv_ms);
}
#else // CONFIG_PROC_SYSCTL
static int do_proc_int_conv_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
int dir, const struct ctl_table *tbl)
{
return -ENOSYS;
}
static int do_proc_int_conv_userhz_jiffies(bool *negp, ulong *u_ptr,
int *k_ptr, int dir,
const struct ctl_table *tbl)
{
return -ENOSYS;
}
static int do_proc_int_conv_ms_jiffies(bool *negp, ulong *u_ptr, int *k_ptr,
int dir, const struct ctl_table *tbl)
{
return -ENOSYS;
}
static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr,
int *k_ptr, int dir,
const struct ctl_table *tbl)
{
return -ENOSYS;
}
#endif
/**
* proc_dointvec_jiffies - read a vector of integers as seconds
+1
View File
@@ -6,6 +6,7 @@ config CEPH_LIB
select CRYPTO_AES
select CRYPTO_CBC
select CRYPTO_GCM
select CRYPTO_KRB5
select CRYPTO_LIB_SHA256
select CRYPTO
select KEYS
+142 -53
View File
@@ -17,6 +17,22 @@
#include "auth_x.h"
#include "auth_x_protocol.h"
static const u32 ticket_key_usages[] = {
CEPHX_KEY_USAGE_TICKET_SESSION_KEY,
CEPHX_KEY_USAGE_TICKET_BLOB,
CEPHX_KEY_USAGE_AUTH_CONNECTION_SECRET
};
static const u32 authorizer_key_usages[] = {
CEPHX_KEY_USAGE_AUTHORIZE,
CEPHX_KEY_USAGE_AUTHORIZE_CHALLENGE,
CEPHX_KEY_USAGE_AUTHORIZE_REPLY
};
static const u32 client_key_usages[] = {
CEPHX_KEY_USAGE_TICKET_SESSION_KEY
};
static void ceph_x_validate_tickets(struct ceph_auth_client *ac, int *pneed);
static int ceph_x_is_authenticated(struct ceph_auth_client *ac)
@@ -44,28 +60,41 @@ static int ceph_x_should_authenticate(struct ceph_auth_client *ac)
return !!need;
}
static int ceph_x_encrypt_offset(void)
static int __ceph_x_encrypt_offset(const struct ceph_crypto_key *key)
{
return sizeof(u32) + sizeof(struct ceph_x_encrypt_header);
return ceph_crypt_data_offset(key) +
sizeof(struct ceph_x_encrypt_header);
}
static int ceph_x_encrypt_buflen(int ilen)
static int ceph_x_encrypt_offset(const struct ceph_crypto_key *key)
{
return ceph_x_encrypt_offset() + ilen + 16;
return sizeof(u32) + __ceph_x_encrypt_offset(key);
}
static int ceph_x_encrypt(struct ceph_crypto_key *secret, void *buf,
int buf_len, int plaintext_len)
/*
* AES: ciphertext_len | hdr | data... | padding
* AES256KRB5: ciphertext_len | confounder | hdr | data... | hmac
*/
static int ceph_x_encrypt_buflen(const struct ceph_crypto_key *key,
int data_len)
{
struct ceph_x_encrypt_header *hdr = buf + sizeof(u32);
int encrypt_len = sizeof(struct ceph_x_encrypt_header) + data_len;
return sizeof(u32) + ceph_crypt_buflen(key, encrypt_len);
}
static int ceph_x_encrypt(const struct ceph_crypto_key *key, int usage_slot,
void *buf, int buf_len, int plaintext_len)
{
struct ceph_x_encrypt_header *hdr;
int ciphertext_len;
int ret;
hdr = buf + sizeof(u32) + ceph_crypt_data_offset(key);
hdr->struct_v = 1;
hdr->magic = cpu_to_le64(CEPHX_ENC_MAGIC);
ret = ceph_crypt(secret, true, buf + sizeof(u32), buf_len - sizeof(u32),
plaintext_len + sizeof(struct ceph_x_encrypt_header),
ret = ceph_crypt(key, usage_slot, true, buf + sizeof(u32),
buf_len - sizeof(u32), plaintext_len + sizeof(*hdr),
&ciphertext_len);
if (ret)
return ret;
@@ -74,18 +103,19 @@ static int ceph_x_encrypt(struct ceph_crypto_key *secret, void *buf,
return sizeof(u32) + ciphertext_len;
}
static int __ceph_x_decrypt(struct ceph_crypto_key *secret, void *p,
int ciphertext_len)
static int __ceph_x_decrypt(const struct ceph_crypto_key *key, int usage_slot,
void *p, int ciphertext_len)
{
struct ceph_x_encrypt_header *hdr = p;
struct ceph_x_encrypt_header *hdr;
int plaintext_len;
int ret;
ret = ceph_crypt(secret, false, p, ciphertext_len, ciphertext_len,
&plaintext_len);
ret = ceph_crypt(key, usage_slot, false, p, ciphertext_len,
ciphertext_len, &plaintext_len);
if (ret)
return ret;
hdr = p + ceph_crypt_data_offset(key);
if (le64_to_cpu(hdr->magic) != CEPHX_ENC_MAGIC) {
pr_err("%s bad magic\n", __func__);
return -EINVAL;
@@ -94,7 +124,8 @@ static int __ceph_x_decrypt(struct ceph_crypto_key *secret, void *p,
return plaintext_len - sizeof(*hdr);
}
static int ceph_x_decrypt(struct ceph_crypto_key *secret, void **p, void *end)
static int ceph_x_decrypt(const struct ceph_crypto_key *key, int usage_slot,
void **p, void *end)
{
int ciphertext_len;
int ret;
@@ -102,7 +133,7 @@ static int ceph_x_decrypt(struct ceph_crypto_key *secret, void **p, void *end)
ceph_decode_32_safe(p, end, ciphertext_len, e_inval);
ceph_decode_need(p, end, ciphertext_len, e_inval);
ret = __ceph_x_decrypt(secret, *p, ciphertext_len);
ret = __ceph_x_decrypt(key, usage_slot, *p, ciphertext_len);
if (ret < 0)
return ret;
@@ -193,8 +224,10 @@ static int process_one_ticket(struct ceph_auth_client *ac,
}
/* blob for me */
dp = *p + ceph_x_encrypt_offset();
ret = ceph_x_decrypt(secret, p, end);
dp = *p + ceph_x_encrypt_offset(secret);
ret = ceph_x_decrypt(secret,
0 /* CEPHX_KEY_USAGE_TICKET_SESSION_KEY */,
p, end);
if (ret < 0)
goto out;
dout(" decrypted %d bytes\n", ret);
@@ -208,6 +241,11 @@ static int process_one_ticket(struct ceph_auth_client *ac,
if (ret)
goto out;
ret = ceph_crypto_key_prepare(&new_session_key, ticket_key_usages,
ARRAY_SIZE(ticket_key_usages));
if (ret)
goto out;
ceph_decode_need(&dp, dend, sizeof(struct ceph_timespec), bad);
ceph_decode_timespec64(&validity, dp);
dp += sizeof(struct ceph_timespec);
@@ -220,8 +258,10 @@ static int process_one_ticket(struct ceph_auth_client *ac,
ceph_decode_8_safe(p, end, is_enc, bad);
if (is_enc) {
/* encrypted */
tp = *p + ceph_x_encrypt_offset();
ret = ceph_x_decrypt(&th->session_key, p, end);
tp = *p + ceph_x_encrypt_offset(&th->session_key);
ret = ceph_x_decrypt(&th->session_key,
1 /* CEPHX_KEY_USAGE_TICKET_BLOB */,
p, end);
if (ret < 0)
goto out;
dout(" encrypted ticket, decrypted %d bytes\n", ret);
@@ -312,7 +352,7 @@ static int encrypt_authorizer(struct ceph_x_authorizer *au,
p = (void *)(msg_a + 1) + le32_to_cpu(msg_a->ticket_blob.blob_len);
end = au->buf->vec.iov_base + au->buf->vec.iov_len;
msg_b = p + ceph_x_encrypt_offset();
msg_b = p + ceph_x_encrypt_offset(&au->session_key);
msg_b->struct_v = 2;
msg_b->nonce = cpu_to_le64(au->nonce);
if (server_challenge) {
@@ -324,7 +364,9 @@ static int encrypt_authorizer(struct ceph_x_authorizer *au,
msg_b->server_challenge_plus_one = 0;
}
ret = ceph_x_encrypt(&au->session_key, p, end - p, sizeof(*msg_b));
ret = ceph_x_encrypt(&au->session_key,
0 /* CEPHX_KEY_USAGE_AUTHORIZE */,
p, end - p, sizeof(*msg_b));
if (ret < 0)
return ret;
@@ -367,8 +409,13 @@ static int ceph_x_build_authorizer(struct ceph_auth_client *ac,
if (ret)
goto out_au;
ret = ceph_crypto_key_prepare(&au->session_key, authorizer_key_usages,
ARRAY_SIZE(authorizer_key_usages));
if (ret)
goto out_au;
maxlen = sizeof(*msg_a) + ticket_blob_len +
ceph_x_encrypt_buflen(sizeof(*msg_b));
ceph_x_encrypt_buflen(&au->session_key, sizeof(*msg_b));
dout(" need len %d\n", maxlen);
if (au->buf && au->buf->alloc_len < maxlen) {
ceph_buffer_put(au->buf);
@@ -506,8 +553,7 @@ static int ceph_x_build_request(struct ceph_auth_client *ac,
if (need & CEPH_ENTITY_TYPE_AUTH) {
struct ceph_x_authenticate *auth = (void *)(head + 1);
void *enc_buf = xi->auth_authorizer.enc_buf;
struct ceph_x_challenge_blob *blob = enc_buf +
ceph_x_encrypt_offset();
struct ceph_x_challenge_blob *blob;
u64 *u;
p = auth + 1;
@@ -517,14 +563,29 @@ static int ceph_x_build_request(struct ceph_auth_client *ac,
dout(" get_auth_session_key\n");
head->op = cpu_to_le16(CEPHX_GET_AUTH_SESSION_KEY);
/* encrypt and hash */
if (xi->secret.type == CEPH_CRYPTO_AES) {
blob = enc_buf + ceph_x_encrypt_offset(&xi->secret);
} else {
BUILD_BUG_ON(SHA256_DIGEST_SIZE + sizeof(*blob) >
CEPHX_AU_ENC_BUF_LEN);
blob = enc_buf + SHA256_DIGEST_SIZE;
}
get_random_bytes(&auth->client_challenge, sizeof(u64));
blob->client_challenge = auth->client_challenge;
blob->server_challenge = cpu_to_le64(xi->server_challenge);
ret = ceph_x_encrypt(&xi->secret, enc_buf, CEPHX_AU_ENC_BUF_LEN,
sizeof(*blob));
if (ret < 0)
return ret;
if (xi->secret.type == CEPH_CRYPTO_AES) {
ret = ceph_x_encrypt(&xi->secret, 0 /* dummy */,
enc_buf, CEPHX_AU_ENC_BUF_LEN,
sizeof(*blob));
if (ret < 0)
return ret;
} else {
ceph_hmac_sha256(&xi->secret, blob, sizeof(*blob),
enc_buf);
ret = SHA256_DIGEST_SIZE;
}
auth->struct_v = 3; /* nautilus+ */
auth->key = 0;
@@ -634,8 +695,10 @@ static int handle_auth_session_key(struct ceph_auth_client *ac, u64 global_id,
ceph_decode_need(p, end, len, e_inval);
dout("%s connection secret blob len %d\n", __func__, len);
if (len > 0) {
dp = *p + ceph_x_encrypt_offset();
ret = ceph_x_decrypt(&th->session_key, p, *p + len);
dp = *p + ceph_x_encrypt_offset(&th->session_key);
ret = ceph_x_decrypt(&th->session_key,
2 /* CEPHX_KEY_USAGE_AUTH_CONNECTION_SECRET */,
p, *p + len);
if (ret < 0)
return ret;
@@ -799,12 +862,14 @@ static int decrypt_authorizer_challenge(struct ceph_crypto_key *secret,
int ret;
/* no leading len */
ret = __ceph_x_decrypt(secret, challenge, challenge_len);
ret = __ceph_x_decrypt(secret,
1 /* CEPHX_KEY_USAGE_AUTHORIZE_CHALLENGE */,
challenge, challenge_len);
if (ret < 0)
return ret;
dout("%s decrypted %d bytes\n", __func__, ret);
dp = challenge + sizeof(struct ceph_x_encrypt_header);
dp = challenge + __ceph_x_encrypt_offset(secret);
dend = dp + ret;
ceph_decode_skip_8(&dp, dend, e_inval); /* struct_v */
@@ -851,8 +916,9 @@ static int decrypt_authorizer_reply(struct ceph_crypto_key *secret,
u8 struct_v;
int ret;
dp = *p + ceph_x_encrypt_offset();
ret = ceph_x_decrypt(secret, p, end);
dp = *p + ceph_x_encrypt_offset(secret);
ret = ceph_x_decrypt(secret, 2 /* CEPHX_KEY_USAGE_AUTHORIZE_REPLY */,
p, end);
if (ret < 0)
return ret;
@@ -974,7 +1040,8 @@ static int calc_signature(struct ceph_x_authorizer *au, struct ceph_msg *msg,
__le32 front_crc;
__le32 middle_crc;
__le32 data_crc;
} __packed *sigblock = enc_buf + ceph_x_encrypt_offset();
} __packed *sigblock = enc_buf +
ceph_x_encrypt_offset(&au->session_key);
sigblock->len = cpu_to_le32(4*sizeof(u32));
sigblock->header_crc = msg->hdr.crc;
@@ -982,8 +1049,9 @@ static int calc_signature(struct ceph_x_authorizer *au, struct ceph_msg *msg,
sigblock->middle_crc = msg->footer.middle_crc;
sigblock->data_crc = msg->footer.data_crc;
ret = ceph_x_encrypt(&au->session_key, enc_buf,
CEPHX_AU_ENC_BUF_LEN, sizeof(*sigblock));
ret = ceph_x_encrypt(&au->session_key, 0 /* dummy */,
enc_buf, CEPHX_AU_ENC_BUF_LEN,
sizeof(*sigblock));
if (ret < 0)
return ret;
@@ -998,11 +1066,19 @@ static int calc_signature(struct ceph_x_authorizer *au, struct ceph_msg *msg,
__le32 data_crc;
__le32 data_len;
__le32 seq_lower_word;
} __packed *sigblock = enc_buf;
} __packed *sigblock;
struct {
__le64 a, b, c, d;
} __packed *penc = enc_buf;
int ciphertext_len;
if (au->session_key.type == CEPH_CRYPTO_AES) {
/* no leading len, no ceph_x_encrypt_header */
sigblock = enc_buf;
} else {
BUILD_BUG_ON(SHA256_DIGEST_SIZE + sizeof(*sigblock) >
CEPHX_AU_ENC_BUF_LEN);
sigblock = enc_buf + SHA256_DIGEST_SIZE;
}
sigblock->header_crc = msg->hdr.crc;
sigblock->front_crc = msg->footer.front_crc;
@@ -1013,12 +1089,18 @@ static int calc_signature(struct ceph_x_authorizer *au, struct ceph_msg *msg,
sigblock->data_len = msg->hdr.data_len;
sigblock->seq_lower_word = *(__le32 *)&msg->hdr.seq;
/* no leading len, no ceph_x_encrypt_header */
ret = ceph_crypt(&au->session_key, true, enc_buf,
CEPHX_AU_ENC_BUF_LEN, sizeof(*sigblock),
&ciphertext_len);
if (ret)
return ret;
if (au->session_key.type == CEPH_CRYPTO_AES) {
int ciphertext_len; /* unused */
ret = ceph_crypt(&au->session_key, 0 /* dummy */,
true, enc_buf, CEPHX_AU_ENC_BUF_LEN,
sizeof(*sigblock), &ciphertext_len);
if (ret)
return ret;
} else {
ceph_hmac_sha256(&au->session_key, sigblock,
sizeof(*sigblock), enc_buf);
}
*psig = penc->a ^ penc->b ^ penc->c ^ penc->d;
}
@@ -1092,21 +1174,27 @@ int ceph_x_init(struct ceph_auth_client *ac)
int ret;
dout("ceph_x_init %p\n", ac);
ret = -ENOMEM;
xi = kzalloc(sizeof(*xi), GFP_NOFS);
if (!xi)
goto out;
return -ENOMEM;
ret = -EINVAL;
if (!ac->key) {
pr_err("no secret set (for auth_x protocol)\n");
goto out_nomem;
goto err_xi;
}
ret = ceph_crypto_key_clone(&xi->secret, ac->key);
if (ret < 0) {
pr_err("cannot clone key: %d\n", ret);
goto out_nomem;
goto err_xi;
}
ret = ceph_crypto_key_prepare(&xi->secret, client_key_usages,
ARRAY_SIZE(client_key_usages));
if (ret) {
pr_err("cannot prepare key: %d\n", ret);
goto err_secret;
}
xi->starting = true;
@@ -1117,8 +1205,9 @@ int ceph_x_init(struct ceph_auth_client *ac)
ac->ops = &ceph_x_ops;
return 0;
out_nomem:
err_secret:
ceph_crypto_key_destroy(&xi->secret);
err_xi:
kfree(xi);
out:
return ret;
}
+38
View File
@@ -6,6 +6,44 @@
#define CEPHX_GET_PRINCIPAL_SESSION_KEY 0x0200
#define CEPHX_GET_ROTATING_KEY 0x0400
/* Client <-> AuthMonitor */
/*
* The AUTH session's connection secret: encrypted with the AUTH
* ticket session key
*/
#define CEPHX_KEY_USAGE_AUTH_CONNECTION_SECRET 0x03
/*
* The ticket's blob for the client ("blob for me", contains the
* session key): encrypted with the client's secret key in case of
* the AUTH ticket and the AUTH ticket session key in case of other
* service tickets
*/
#define CEPHX_KEY_USAGE_TICKET_SESSION_KEY 0x04
/*
* The ticket's blob for the service (ceph_x_ticket_blob): possibly
* encrypted with the old AUTH ticket session key in case of the AUTH
* ticket and not encrypted in case of other service tickets
*/
#define CEPHX_KEY_USAGE_TICKET_BLOB 0x05
/* Client <-> Service */
/*
* The client's authorization request (ceph_x_authorize_b):
* encrypted with the service ticket session key
*/
#define CEPHX_KEY_USAGE_AUTHORIZE 0x10
/*
* The service's challenge (ceph_x_authorize_challenge):
* encrypted with the service ticket session key
*/
#define CEPHX_KEY_USAGE_AUTHORIZE_CHALLENGE 0x11
/*
* The service's final reply (ceph_x_authorize_reply + the service
* session's connection secret): encrypted with the service ticket
* session key
*/
#define CEPHX_KEY_USAGE_AUTHORIZE_REPLY 0x12
/* common bits */
struct ceph_x_ticket_blob {
__u8 struct_v;
+224 -53
View File
@@ -7,6 +7,7 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <crypto/aes.h>
#include <crypto/krb5.h>
#include <crypto/skcipher.h>
#include <linux/key-type.h>
#include <linux/sched/mm.h>
@@ -16,77 +17,119 @@
#include <linux/ceph/decode.h>
#include "crypto.h"
/*
* Set ->key and ->tfm. The rest of the key should be filled in before
* this function is called.
*/
static int set_secret(struct ceph_crypto_key *key, void *buf)
static int set_aes_tfm(struct ceph_crypto_key *key)
{
unsigned int noio_flag;
int ret;
key->key = NULL;
key->tfm = NULL;
noio_flag = memalloc_noio_save();
key->aes_tfm = crypto_alloc_sync_skcipher("cbc(aes)", 0, 0);
memalloc_noio_restore(noio_flag);
if (IS_ERR(key->aes_tfm)) {
ret = PTR_ERR(key->aes_tfm);
key->aes_tfm = NULL;
return ret;
}
ret = crypto_sync_skcipher_setkey(key->aes_tfm, key->key, key->len);
if (ret)
return ret;
return 0;
}
static int set_krb5_tfms(struct ceph_crypto_key *key, const u32 *key_usages,
int key_usage_cnt)
{
struct krb5_buffer TK = { .len = key->len, .data = key->key };
unsigned int noio_flag;
int ret = 0;
int i;
if (WARN_ON_ONCE(key_usage_cnt > ARRAY_SIZE(key->krb5_tfms)))
return -EINVAL;
key->krb5_type = crypto_krb5_find_enctype(
KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192);
if (!key->krb5_type)
return -ENOPKG;
/*
* Despite crypto_krb5_prepare_encryption() taking a gfp mask,
* crypto_alloc_aead() inside of it allocates with GFP_KERNEL.
*/
noio_flag = memalloc_noio_save();
for (i = 0; i < key_usage_cnt; i++) {
key->krb5_tfms[i] = crypto_krb5_prepare_encryption(
key->krb5_type, &TK, key_usages[i],
GFP_NOIO);
if (IS_ERR(key->krb5_tfms[i])) {
ret = PTR_ERR(key->krb5_tfms[i]);
key->krb5_tfms[i] = NULL;
goto out_flag;
}
}
out_flag:
memalloc_noio_restore(noio_flag);
return ret;
}
int ceph_crypto_key_prepare(struct ceph_crypto_key *key,
const u32 *key_usages, int key_usage_cnt)
{
switch (key->type) {
case CEPH_CRYPTO_NONE:
return 0; /* nothing to do */
case CEPH_CRYPTO_AES:
break;
return set_aes_tfm(key);
case CEPH_CRYPTO_AES256KRB5:
hmac_sha256_preparekey(&key->hmac_key, key->key, key->len);
return set_krb5_tfms(key, key_usages, key_usage_cnt);
default:
return -ENOTSUPP;
}
if (!key->len)
return -EINVAL;
key->key = kmemdup(buf, key->len, GFP_NOIO);
if (!key->key) {
ret = -ENOMEM;
goto fail;
}
/* crypto_alloc_sync_skcipher() allocates with GFP_KERNEL */
noio_flag = memalloc_noio_save();
key->tfm = crypto_alloc_sync_skcipher("cbc(aes)", 0, 0);
memalloc_noio_restore(noio_flag);
if (IS_ERR(key->tfm)) {
ret = PTR_ERR(key->tfm);
key->tfm = NULL;
goto fail;
}
ret = crypto_sync_skcipher_setkey(key->tfm, key->key, key->len);
if (ret)
goto fail;
return 0;
fail:
ceph_crypto_key_destroy(key);
return ret;
}
/*
* @dst should be zeroed before this function is called.
*/
int ceph_crypto_key_clone(struct ceph_crypto_key *dst,
const struct ceph_crypto_key *src)
{
memcpy(dst, src, sizeof(struct ceph_crypto_key));
return set_secret(dst, src->key);
dst->type = src->type;
dst->created = src->created;
dst->len = src->len;
dst->key = kmemdup(src->key, src->len, GFP_NOIO);
if (!dst->key)
return -ENOMEM;
return 0;
}
/*
* @key should be zeroed before this function is called.
*/
int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end)
{
int ret;
ceph_decode_need(p, end, 2*sizeof(u16) + sizeof(key->created), bad);
key->type = ceph_decode_16(p);
ceph_decode_copy(p, &key->created, sizeof(key->created));
key->len = ceph_decode_16(p);
ceph_decode_need(p, end, key->len, bad);
ret = set_secret(key, *p);
if (key->len > CEPH_MAX_KEY_LEN) {
pr_err("secret too big %d\n", key->len);
return -EINVAL;
}
key->key = kmemdup(*p, key->len, GFP_NOIO);
if (!key->key)
return -ENOMEM;
memzero_explicit(*p, key->len);
*p += key->len;
return ret;
return 0;
bad:
dout("failed to decode crypto key\n");
@@ -122,12 +165,26 @@ int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *inkey)
void ceph_crypto_key_destroy(struct ceph_crypto_key *key)
{
if (key) {
kfree_sensitive(key->key);
key->key = NULL;
if (key->tfm) {
crypto_free_sync_skcipher(key->tfm);
key->tfm = NULL;
int i;
if (!key)
return;
kfree_sensitive(key->key);
key->key = NULL;
if (key->type == CEPH_CRYPTO_AES) {
if (key->aes_tfm) {
crypto_free_sync_skcipher(key->aes_tfm);
key->aes_tfm = NULL;
}
} else if (key->type == CEPH_CRYPTO_AES256KRB5) {
memzero_explicit(&key->hmac_key, sizeof(key->hmac_key));
for (i = 0; i < ARRAY_SIZE(key->krb5_tfms); i++) {
if (key->krb5_tfms[i]) {
crypto_free_aead(key->krb5_tfms[i]);
key->krb5_tfms[i] = NULL;
}
}
}
}
@@ -207,7 +264,7 @@ static void teardown_sgtable(struct sg_table *sgt)
static int ceph_aes_crypt(const struct ceph_crypto_key *key, bool encrypt,
void *buf, int buf_len, int in_len, int *pout_len)
{
SYNC_SKCIPHER_REQUEST_ON_STACK(req, key->tfm);
SYNC_SKCIPHER_REQUEST_ON_STACK(req, key->aes_tfm);
struct sg_table sgt;
struct scatterlist prealloc_sg;
char iv[AES_BLOCK_SIZE] __aligned(8);
@@ -223,7 +280,7 @@ static int ceph_aes_crypt(const struct ceph_crypto_key *key, bool encrypt,
return ret;
memcpy(iv, aes_iv, AES_BLOCK_SIZE);
skcipher_request_set_sync_tfm(req, key->tfm);
skcipher_request_set_sync_tfm(req, key->aes_tfm);
skcipher_request_set_callback(req, 0, NULL, NULL);
skcipher_request_set_crypt(req, sgt.sgl, sgt.sgl, crypt_len, iv);
@@ -268,7 +325,68 @@ out_sgt:
return ret;
}
int ceph_crypt(const struct ceph_crypto_key *key, bool encrypt,
static int ceph_krb5_encrypt(const struct ceph_crypto_key *key, int usage_slot,
void *buf, int buf_len, int in_len, int *pout_len)
{
struct sg_table sgt;
struct scatterlist prealloc_sg;
int ret;
if (WARN_ON_ONCE(usage_slot >= ARRAY_SIZE(key->krb5_tfms)))
return -EINVAL;
ret = setup_sgtable(&sgt, &prealloc_sg, buf, buf_len);
if (ret)
return ret;
ret = crypto_krb5_encrypt(key->krb5_type, key->krb5_tfms[usage_slot],
sgt.sgl, sgt.nents, buf_len, AES_BLOCK_SIZE,
in_len, false);
if (ret < 0) {
pr_err("%s encrypt failed: %d\n", __func__, ret);
goto out_sgt;
}
*pout_len = ret;
ret = 0;
out_sgt:
teardown_sgtable(&sgt);
return ret;
}
static int ceph_krb5_decrypt(const struct ceph_crypto_key *key, int usage_slot,
void *buf, int buf_len, int in_len, int *pout_len)
{
struct sg_table sgt;
struct scatterlist prealloc_sg;
size_t data_off = 0;
size_t data_len = in_len;
int ret;
if (WARN_ON_ONCE(usage_slot >= ARRAY_SIZE(key->krb5_tfms)))
return -EINVAL;
ret = setup_sgtable(&sgt, &prealloc_sg, buf, in_len);
if (ret)
return ret;
ret = crypto_krb5_decrypt(key->krb5_type, key->krb5_tfms[usage_slot],
sgt.sgl, sgt.nents, &data_off, &data_len);
if (ret) {
pr_err("%s decrypt failed: %d\n", __func__, ret);
goto out_sgt;
}
WARN_ON(data_off != AES_BLOCK_SIZE);
*pout_len = data_len;
out_sgt:
teardown_sgtable(&sgt);
return ret;
}
int ceph_crypt(const struct ceph_crypto_key *key, int usage_slot, bool encrypt,
void *buf, int buf_len, int in_len, int *pout_len)
{
switch (key->type) {
@@ -278,11 +396,64 @@ int ceph_crypt(const struct ceph_crypto_key *key, bool encrypt,
case CEPH_CRYPTO_AES:
return ceph_aes_crypt(key, encrypt, buf, buf_len, in_len,
pout_len);
case CEPH_CRYPTO_AES256KRB5:
return encrypt ?
ceph_krb5_encrypt(key, usage_slot, buf, buf_len, in_len,
pout_len) :
ceph_krb5_decrypt(key, usage_slot, buf, buf_len, in_len,
pout_len);
default:
return -ENOTSUPP;
}
}
int ceph_crypt_data_offset(const struct ceph_crypto_key *key)
{
switch (key->type) {
case CEPH_CRYPTO_NONE:
case CEPH_CRYPTO_AES:
return 0;
case CEPH_CRYPTO_AES256KRB5:
/* confounder */
return AES_BLOCK_SIZE;
default:
BUG();
}
}
int ceph_crypt_buflen(const struct ceph_crypto_key *key, int data_len)
{
switch (key->type) {
case CEPH_CRYPTO_NONE:
return data_len;
case CEPH_CRYPTO_AES:
/* PKCS#7 padding at the end */
return data_len + AES_BLOCK_SIZE -
(data_len & (AES_BLOCK_SIZE - 1));
case CEPH_CRYPTO_AES256KRB5:
/* confounder at the beginning and 192-bit HMAC at the end */
return AES_BLOCK_SIZE + data_len + 24;
default:
BUG();
}
}
void ceph_hmac_sha256(const struct ceph_crypto_key *key, const void *buf,
int buf_len, u8 hmac[SHA256_DIGEST_SIZE])
{
switch (key->type) {
case CEPH_CRYPTO_NONE:
case CEPH_CRYPTO_AES:
memset(hmac, 0, SHA256_DIGEST_SIZE);
return;
case CEPH_CRYPTO_AES256KRB5:
hmac_sha256(&key->hmac_key, buf, buf_len, hmac);
return;
default:
BUG();
}
}
static int ceph_key_preparse(struct key_preparsed_payload *prep)
{
struct ceph_crypto_key *ckey;
@@ -295,7 +466,7 @@ static int ceph_key_preparse(struct key_preparsed_payload *prep)
goto err;
ret = -ENOMEM;
ckey = kmalloc(sizeof(*ckey), GFP_KERNEL);
ckey = kzalloc(sizeof(*ckey), GFP_KERNEL);
if (!ckey)
goto err;
+18 -3
View File
@@ -2,10 +2,11 @@
#ifndef _FS_CEPH_CRYPTO_H
#define _FS_CEPH_CRYPTO_H
#include <crypto/sha2.h>
#include <linux/ceph/types.h>
#include <linux/ceph/buffer.h>
#define CEPH_KEY_LEN 16
#define CEPH_MAX_KEY_LEN 32
#define CEPH_MAX_CON_SECRET_LEN 64
/*
@@ -16,9 +17,19 @@ struct ceph_crypto_key {
struct ceph_timespec created;
int len;
void *key;
struct crypto_sync_skcipher *tfm;
union {
struct crypto_sync_skcipher *aes_tfm;
struct {
struct hmac_sha256_key hmac_key;
const struct krb5_enctype *krb5_type;
struct crypto_aead *krb5_tfms[3];
};
};
};
int ceph_crypto_key_prepare(struct ceph_crypto_key *key,
const u32 *key_usages, int key_usage_cnt);
int ceph_crypto_key_clone(struct ceph_crypto_key *dst,
const struct ceph_crypto_key *src);
int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end);
@@ -26,8 +37,12 @@ int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *in);
void ceph_crypto_key_destroy(struct ceph_crypto_key *key);
/* crypto.c */
int ceph_crypt(const struct ceph_crypto_key *key, bool encrypt,
int ceph_crypt(const struct ceph_crypto_key *key, int usage_slot, bool encrypt,
void *buf, int buf_len, int in_len, int *pout_len);
int ceph_crypt_data_offset(const struct ceph_crypto_key *key);
int ceph_crypt_buflen(const struct ceph_crypto_key *key, int data_len);
void ceph_hmac_sha256(const struct ceph_crypto_key *key, const void *buf,
int buf_len, u8 hmac[SHA256_DIGEST_SIZE]);
int ceph_crypto_init(void);
void ceph_crypto_shutdown(void);
+8 -8
View File
@@ -779,9 +779,9 @@ static int setup_crypto(struct ceph_connection *con,
return 0; /* auth_x, secure mode */
}
static void ceph_hmac_sha256(struct ceph_connection *con,
const struct kvec *kvecs, int kvec_cnt,
u8 hmac[SHA256_DIGEST_SIZE])
static void con_hmac_sha256(struct ceph_connection *con,
const struct kvec *kvecs, int kvec_cnt,
u8 hmac[SHA256_DIGEST_SIZE])
{
struct hmac_sha256_ctx ctx;
int i;
@@ -1438,8 +1438,8 @@ static int prepare_auth_signature(struct ceph_connection *con)
if (!buf)
return -ENOMEM;
ceph_hmac_sha256(con, con->v2.in_sign_kvecs, con->v2.in_sign_kvec_cnt,
CTRL_BODY(buf));
con_hmac_sha256(con, con->v2.in_sign_kvecs, con->v2.in_sign_kvec_cnt,
CTRL_BODY(buf));
return prepare_control(con, FRAME_TAG_AUTH_SIGNATURE, buf,
SHA256_DIGEST_SIZE);
@@ -2360,7 +2360,7 @@ bad:
*/
static int process_auth_done(struct ceph_connection *con, void *p, void *end)
{
u8 session_key_buf[CEPH_KEY_LEN + 16];
u8 session_key_buf[CEPH_MAX_KEY_LEN + 16];
u8 con_secret_buf[CEPH_MAX_CON_SECRET_LEN + 16];
u8 *session_key = PTR_ALIGN(&session_key_buf[0], 16);
u8 *con_secret = PTR_ALIGN(&con_secret_buf[0], 16);
@@ -2436,8 +2436,8 @@ static int process_auth_signature(struct ceph_connection *con,
return -EINVAL;
}
ceph_hmac_sha256(con, con->v2.out_sign_kvecs, con->v2.out_sign_kvec_cnt,
hmac);
con_hmac_sha256(con, con->v2.out_sign_kvecs, con->v2.out_sign_kvec_cnt,
hmac);
ceph_decode_need(&p, end, SHA256_DIGEST_SIZE, bad);
if (crypto_memneq(p, hmac, SHA256_DIGEST_SIZE)) {
+1 -1
View File
@@ -5,7 +5,7 @@ cat << "END" | $@ -Werror -Wl,--fatal-warnings -x c - -o /dev/null >/dev/null 2>
#include <stdio.h>
int main(void)
{
printf("");
printf("\n");
return 0;
}
END
+1 -1
View File
@@ -54,7 +54,7 @@ COMP_TOOLS = {
'bzip2': CompTool('.bz2', 'pbzip2,bzip2'),
'gzip': CompTool('.gz', 'pigz,gzip'),
'lz4': CompTool('.lz4', 'lz4'),
'lzma': CompTool('.lzma', 'plzip,lzma'),
'lzma': CompTool('.lzma', 'lzma'),
'lzo': CompTool('.lzo', 'lzop'),
'xz': CompTool('.xz', 'xz'),
'zstd': CompTool('.zstd', 'zstd'),
+55 -9
View File
@@ -2,8 +2,6 @@
%{!?_arch: %define _arch dummy}
%{!?make: %define make make}
%define makeflags %{?_smp_mflags} ARCH=%{ARCH}
%define __spec_install_post /usr/lib/rpm/brp-compress || :
%define debug_package %{nil}
Name: kernel
Summary: The Linux Kernel
@@ -47,14 +45,50 @@ This package provides kernel headers and makefiles sufficient to build modules
against the %{version} kernel package.
%endif
%if %{with_debuginfo}
%if %{with_debuginfo_manual}
%package debuginfo
Summary: Debug information package for the Linux kernel
Group: Development/Debug
AutoReq: 0
AutoProv: 1
%description debuginfo
This package provides debug information for the kernel image and modules from the
%{version} package.
%define install_mod_strip 1
%endif
%if %{with_debuginfo_rpm}
# list of debuginfo-related options taken from distribution kernel.spec
# files
%undefine _include_minidebuginfo
%undefine _find_debuginfo_dwz_opts
%undefine _unique_build_ids
%undefine _unique_debug_names
%undefine _unique_debug_srcs
%undefine _debugsource_packages
%undefine _debuginfo_subpackages
%global _find_debuginfo_opts -r
%global _missing_build_ids_terminate_build 1
%global _no_recompute_build_ids 1
%{debug_package}
# later, we make all modules executable so that find-debuginfo.sh strips
# them up. but they don't actually need to be executable, so remove the
# executable bit, taking care to do it _after_ find-debuginfo.sh has run
%define __spec_install_post \
%{?__debug_package:%{__debug_install_post}} \
%{__arch_install_post} \
%{__os_install_post} \
find %{buildroot}/lib/modules/%{KERNELRELEASE} -name "*.ko" -type f \\\
| xargs --no-run-if-empty chmod u-x
%else
%define __spec_install_post /usr/lib/rpm/brp-compress || :
%endif
# some (but not all) versions of rpmbuild emit %%debug_package with
# %%install. since we've already emitted it manually, that would cause
# a package redefinition error. ensure that doesn't happen
%define debug_package %{nil}
%prep
%setup -q -n linux
cp %{SOURCE1} .config
@@ -67,7 +101,7 @@ patch -p1 < %{SOURCE2}
mkdir -p %{buildroot}/lib/modules/%{KERNELRELEASE}
cp $(%{make} %{makeflags} -s image_name) %{buildroot}/lib/modules/%{KERNELRELEASE}/vmlinuz
# DEPMOD=true makes depmod no-op. We do not package depmod-generated files.
%{make} %{makeflags} INSTALL_MOD_PATH=%{buildroot} INSTALL_MOD_STRIP=1 DEPMOD=true modules_install
%{make} %{makeflags} INSTALL_MOD_PATH=%{buildroot} %{?install_mod_strip:INSTALL_MOD_STRIP=1} DEPMOD=true modules_install
%{make} %{makeflags} INSTALL_HDR_PATH=%{buildroot}/usr headers_install
cp System.map %{buildroot}/lib/modules/%{KERNELRELEASE}
cp .config %{buildroot}/lib/modules/%{KERNELRELEASE}/config
@@ -98,22 +132,30 @@ ln -fns /usr/src/kernels/%{KERNELRELEASE} %{buildroot}/lib/modules/%{KERNELRELEA
echo "%exclude /lib/modules/%{KERNELRELEASE}/build"
} > %{buildroot}/kernel.list
%if %{with_debuginfo}
%if 0%{with_debuginfo_manual}%{with_debuginfo_rpm} > 0
# copying vmlinux directly to the debug directory means it will not get
# stripped (but its source paths will still be collected + fixed up)
mkdir -p %{buildroot}/usr/lib/debug/lib/modules/%{KERNELRELEASE}
cp vmlinux %{buildroot}/usr/lib/debug/lib/modules/%{KERNELRELEASE}
%endif
%if %{with_debuginfo_rpm}
# make modules executable so that find-debuginfo.sh strips them. this
# will be undone later in %%__spec_install_post
find %{buildroot}/lib/modules/%{KERNELRELEASE} -name "*.ko" -type f \
| xargs --no-run-if-empty chmod u+x
%endif
%if %{with_debuginfo_manual}
echo /usr/lib/debug/lib/modules/%{KERNELRELEASE}/vmlinux > %{buildroot}/debuginfo.list
while read -r mod; do
mod="${mod%.o}.ko"
dbg="%{buildroot}/usr/lib/debug/lib/modules/%{KERNELRELEASE}/kernel/${mod}"
buildid=$("${READELF}" -n "${mod}" | sed -n 's@^.*Build ID: \(..\)\(.*\)@\1/\2@p')
buildid=$("${READELF:-readelf}" -n "${mod}" | sed -n 's@^.*Build ID: \(..\)\(.*\)@\1/\2@p')
link="%{buildroot}/usr/lib/debug/.build-id/${buildid}.debug"
mkdir -p "${dbg%/*}" "${link%/*}"
"${OBJCOPY}" --only-keep-debug "${mod}" "${dbg}"
"${OBJCOPY:-objcopy}" --only-keep-debug "${mod}" "${dbg}"
ln -sf --relative "${dbg}" "${link}"
echo "${dbg#%{buildroot}}" >> %{buildroot}/debuginfo.list
@@ -123,6 +165,10 @@ done < modules.order
%clean
rm -rf %{buildroot}
%if %{with_debuginfo_rpm}
rm -f debugfiles.list debuglinks.list debugsourcefiles.list debugsources.list \
elfbins.list
%endif
%post
if [ -x /usr/bin/kernel-install ]; then
@@ -162,7 +208,7 @@ fi
/lib/modules/%{KERNELRELEASE}/build
%endif
%if %{with_debuginfo}
%if %{with_debuginfo_manual}
%files -f %{buildroot}/debuginfo.list debuginfo
%defattr (-, root, root)
%exclude /debuginfo.list
+35 -3
View File
@@ -23,15 +23,47 @@ else
echo '%define with_devel 0'
fi
# use %{debug_package} machinery to generate -debuginfo
with_debuginfo_rpm=0
# manually generate -debuginfo package
with_debuginfo_manual=0
# debuginfo package generation uses find-debuginfo.sh under the hood,
# which only works on uncompressed modules that contain debuginfo
if grep -q CONFIG_DEBUG_INFO=y include/config/auto.conf &&
(! grep -q CONFIG_MODULE_COMPRESS=y include/config/auto.conf) &&
(! grep -q CONFIG_DEBUG_INFO_SPLIT=y include/config/auto.conf); then
echo '%define with_debuginfo %{?_without_debuginfo: 0} %{?!_without_debuginfo: 1}'
else
echo '%define with_debuginfo 0'
# If module signing is enabled (which may be required to boot with
# lockdown enabled), the find-debuginfo.sh machinery cannot be used
# because the signatures will be stripped off the modules. However, due
# to an rpm bug in versions prior to 4.20.0
#
# https://github.com/rpm-software-management/rpm/issues/3057
# https://github.com/rpm-software-management/rpm/commit/49f906998f3cf1f4152162ca61ac0869251c380f
#
# We cannot provide our own debuginfo package because it does not listen
# to our custom files list, failing the build due to unpackaged files.
# Manually generate the debug info package if using rpm 4.20.0. If not
# using rpm 4.20.0, avoid generating a -debuginfo package altogether,
# as it is not safe.
if grep -q CONFIG_MODULE_SIG=y include/config/auto.conf; then
rpm_ver_str=$(rpm --version 2>/dev/null)
# Split the version on spaces
IFS=' '
set -- $rpm_ver_str
if [ "${1:-}" = RPM -a "${2:-}" = version ]; then
IFS=.
set -- $3
rpm_ver=$(( 1000000 * $1 + 10000 * $2 + 100 * $3 + ${4:-0} ))
if [ "$rpm_ver" -ge 4200000 ]; then
with_debuginfo_manual='%{?_without_debuginfo:0}%{?!_without_debuginfo:1}'
fi
fi
else
with_debuginfo_rpm='%{?_without_debuginfo:0}%{?!_without_debuginfo:1}'
fi
fi
echo "%define with_debuginfo_manual $with_debuginfo_manual"
echo "%define with_debuginfo_rpm $with_debuginfo_rpm"
cat<<EOF
%define ARCH ${ARCH}
+16 -21
View File
@@ -53,32 +53,29 @@ static DEFINE_SPINLOCK(pinned_root_spinlock);
static bool deny_reading_verity_digests;
#endif
// initialized to false
static bool loadpin_root_writable;
#ifdef CONFIG_SYSCTL
static struct ctl_table loadpin_sysctl_table[] = {
static int proc_handler_loadpin(const struct ctl_table *table, int dir,
void *buffer, size_t *lenp, loff_t *ppos)
{
if (!loadpin_root_writable && SYSCTL_USER_TO_KERN(dir))
return -EINVAL;
return proc_dointvec_minmax(table, dir, buffer, lenp, ppos);
}
static const struct ctl_table loadpin_sysctl_table[] = {
{
.procname = "enforce",
.data = &enforce,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = proc_dointvec_minmax,
.extra1 = SYSCTL_ONE,
.proc_handler = proc_handler_loadpin,
.extra1 = SYSCTL_ZERO,
.extra2 = SYSCTL_ONE,
},
};
static void set_sysctl(bool is_writable)
{
/*
* If load pinning is not enforced via a read-only block
* device, allow sysctl to change modes for testing.
*/
if (is_writable)
loadpin_sysctl_table[0].extra1 = SYSCTL_ZERO;
else
loadpin_sysctl_table[0].extra1 = SYSCTL_ONE;
}
#else
static inline void set_sysctl(bool is_writable) { }
#endif
static void report_writable(struct super_block *mnt_sb, bool writable)
@@ -132,7 +129,6 @@ static int loadpin_check(struct file *file, enum kernel_read_file_id id)
struct super_block *load_root;
const char *origin = kernel_read_file_id_str(id);
bool first_root_pin = false;
bool load_root_writable;
/* If the file id is excluded, ignore the pinning. */
if ((unsigned int)id < ARRAY_SIZE(ignore_read_file_id) &&
@@ -153,7 +149,6 @@ static int loadpin_check(struct file *file, enum kernel_read_file_id id)
}
load_root = file->f_path.mnt->mnt_sb;
load_root_writable = sb_is_writable(load_root);
/* First loaded module/firmware defines the root for all others. */
spin_lock(&pinned_root_spinlock);
@@ -169,8 +164,8 @@ static int loadpin_check(struct file *file, enum kernel_read_file_id id)
spin_unlock(&pinned_root_spinlock);
if (first_root_pin) {
report_writable(pinned_root, load_root_writable);
set_sysctl(load_root_writable);
loadpin_root_writable = sb_is_writable(pinned_root);
report_writable(pinned_root, loadpin_root_writable);
report_load(origin, file, "pinned");
}
+2
View File
@@ -29,6 +29,8 @@ srctree := $(patsubst %/,%,$(dir $(CURDIR)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
endif
RM ?= rm -f
LIBSUBCMD_DIR = $(srctree)/tools/lib/subcmd/
ifneq ($(OUTPUT),)
LIBSUBCMD_OUTPUT = $(abspath $(OUTPUT))/libsubcmd
+15 -5
View File
@@ -111,10 +111,14 @@ The column name "all" can be used to enable all disabled-by-default built-in cou
.PP
\fB--no-perf\fP Disable all the uses of the perf API.
.PP
\fB--force\fP Force turbostat to run on an unsupported platform (minimal defaults).
.PP
\fB--interval seconds\fP overrides the default 5.0 second measurement interval.
.PP
\fB--num_iterations num\fP number of the measurement iterations.
.PP
\fB--header_iterations num\fP print header every num iterations.
.PP
\fB--out output_file\fP turbostat output is written to the specified output_file.
The file is truncated if it already exists, and it is created if it does not exist.
.PP
@@ -159,15 +163,19 @@ The system configuration dump (if --quiet is not used) is followed by statistics
.PP
\fBSMI\fP The number of System Management Interrupts serviced CPU during the measurement interval. While this counter is actually per-CPU, SMI are triggered on all processors, so the number should be the same for all CPUs.
.PP
\fBLLCkRPS\fP Last Level Cache Thousands of References Per Second. For CPUs with an L3 LLC, this is the number of references that CPU made to the L3 (and the number of misses that CPU made to it's L2). For CPUs with an L2 LLC, this is the number of references to the L2 (and the number of misses to the CPU's L1). The system summary row shows the sum for all CPUs. In both cases, the value displayed is the actual value divided by 1000 in the interest of usually fitting into 8 columns.
\fBLLCMRPS\fP Last Level Cache Millions of References Per Second. For CPUs with an L3 LLC, this is the number of references that CPU made to the L3 (and the number of misses that CPU made to it's L2). For CPUs with an L2 LLC, this is the number of references to the L2 (and the number of misses to the CPU's L1). The system summary row shows the sum for all CPUs. In both cases, the value displayed is the actual value divided by 1,000,000. If this value is large, then the LLC%hit column is significant. If this value is small, then the LLC%hit column is not significant.
.PP
\fBLLC%hit\fP Last Level Cache Hit Rate %. Hit Rate Percent = 100.0 * (References - Misses)/References. The system summary row shows the weighted average for all CPUs (100.0 * (Sum_References - Sum_Misses)/Sum_References).
\fBLLC%hit\fP Last Level Cache Hit Rate %. Hit Rate Percent = 100.0 * Hits/References. The system summary row shows the weighted average for all CPUs (100.0 * Sum_Hits/Sum_References).
.PP
\fBL2MRPS\fP Level-2 Cache Millions of References Per Second. For CPUs with an L2 LLC, this is the same as LLC references. The system summary row shows the sum for all CPUs. In both cases, the value displayed is the actual value divided by 1,000,000. If this value is large, then the L2%hit column is significant. If this value is small, then the L2%hit column is not significant.
.PP
\fBL2%hit\fP Level-2 Cache Hit Rate %. Hit Rate Percent = 100.0 * Hits/References. The system summary row shows the weighted average for all CPUs (100.0 * (Sum_Hits)/Sum_References).
.PP
\fBC1, C2, C3...\fP The number times Linux requested the C1, C2, C3 idle state during the measurement interval. The system summary line shows the sum for all CPUs. These are C-state names as exported in /sys/devices/system/cpu/cpu*/cpuidle/state*/name. While their names are generic, their attributes are processor specific. They the system description section of output shows what MWAIT sub-states they are mapped to on each system. These counters are in the "cpuidle" group, which is disabled, by default.
.PP
\fBC1+, C2+, C3+...\fP The idle governor idle state misprediction statistics. Inidcates the number times Linux requested the C1, C2, C3 idle state during the measurement interval, but should have requested a deeper idle state (if it exists and enabled). These statistics come from the /sys/devices/system/cpu/cpu*/cpuidle/state*/below file. These counters are in the "cpuidle" group, which is disabled, by default.
\fBC1+, C2+, C3+...\fP The idle governor idle state misprediction statistics. Indicates the number times Linux requested the C1, C2, C3 idle state during the measurement interval, but should have requested a deeper idle state (if it exists and enabled). These statistics come from the /sys/devices/system/cpu/cpu*/cpuidle/state*/below file. These counters are in the "cpuidle" group, which is disabled, by default.
.PP
\fBC1-, C2-, C3-...\fP The idle governor idle state misprediction statistics. Inidcates the number times Linux requested the C1, C2, C3 idle state during the measurement interval, but should have requested a shallower idle state (if it exists and enabled). These statistics come from the /sys/devices/system/cpu/cpu*/cpuidle/state*/above file. These counters are in the "cpuidle" group, which is disabled, by default.
\fBC1-, C2-, C3-...\fP The idle governor idle state misprediction statistics. Indicates the number times Linux requested the C1, C2, C3 idle state during the measurement interval, but should have requested a shallower idle state (if it exists and enabled). These statistics come from the /sys/devices/system/cpu/cpu*/cpuidle/state*/above file. These counters are in the "cpuidle" group, which is disabled, by default.
.PP
\fBC1%, C2%, C3%\fP The residency percentage that Linux requested C1, C2, C3.... The system summary is the average of all CPUs in the system. Note that these are software, reflecting what was requested. The hardware counters reflect what was actually achieved. These counters are in the "pct_idle" group, which is enabled by default.
.PP
@@ -197,7 +205,7 @@ The system configuration dump (if --quiet is not used) is followed by statistics
.PP
\fBGFX%C0\fP Percentage of time that at least one GFX compute engine is busy.
.PP
\fBCPUGFX%\fP Percentage of time that at least one CPU is busy at the same time as at least one Graphics compute enginer is busy.
\fBCPUGFX%\fP Percentage of time that at least one CPU is busy at the same time as at least one Graphics compute engine is busy.
.PP
\fBPkg%pc2, Pkg%pc3, Pkg%pc6, Pkg%pc7\fP percentage residency in hardware package idle states. These numbers are from hardware residency counters.
.PP
@@ -559,6 +567,8 @@ If the upstream version isn't new enough, the development tree can be found here
If the development tree doesn't work, please contact the author via chat,
or via email with the word "turbostat" on the Subject line.
An old turbostat binary may run on unknown hardware by using "--force",
but results are unsupported.
.SH FILES
.ta
.nf
File diff suppressed because it is too large. Load diff