]> git.apps.os.sepia.ceph.com Git - ceph-client.git/commitdiff
ceph fs debugfs code
authorAlex Markuze <amarkuze@redhat.com>
Tue, 29 Jul 2025 14:23:05 +0000 (14:23 +0000)
committerAlex Markuze <amarkuze@redhat.com>
Thu, 31 Jul 2025 11:07:55 +0000 (11:07 +0000)
fs/ceph/debugfs.c
fs/ceph/super.h
include/linux/ceph/ceph_san_des.h
include/linux/ceph/ceph_san_logger.h
include/linux/ceph/ceph_san_ser.h
net/ceph/ceph_san_des.c
net/ceph/ceph_san_logger.c

index 7dac6d6eaed007e967683c44a0c85c712014ca9e..2ffb2910817652c9b3d2b1a21d71f2c14f75eead 100644 (file)
@@ -9,8 +9,15 @@
 #include <linux/seq_file.h>
 #include <linux/math64.h>
 #include <linux/ktime.h>
+#include <linux/jiffies.h>
+#include <linux/timekeeping.h>
+#include <linux/rtc.h>
+#include <linux/printk.h>
+#include <linux/time.h>
+#include <linux/time_types.h>
 
 #include <linux/ceph/libceph.h>
+#include <linux/ceph/ceph_san_logger.h>
 #include <linux/ceph/mon_client.h>
 #include <linux/ceph/auth.h>
 #include <linux/ceph/debugfs.h>
@@ -349,6 +356,39 @@ static int mds_sessions_show(struct seq_file *s, void *ptr)
        return 0;
 }
 
+/* @buffer: The buffer to store the formatted date and time string.
+ * @buffer_len: The length of the buffer.
+ *
+ * Returns: The number of characters written to the buffer, or a negative error code.
+ */
+static int jiffies_to_formatted_time(unsigned long jiffies_value, char *buffer, size_t buffer_len)
+{
+    unsigned long seconds;
+    unsigned long subsec_jiffies;
+    unsigned long microseconds;
+    struct tm tm_time;
+    time64_t timestamp;
+
+    // Convert jiffies to seconds since boot
+    seconds = jiffies_value / HZ;
+
+    // Calculate remaining jiffies for subsecond precision
+    subsec_jiffies = jiffies_value % HZ;
+    microseconds = (subsec_jiffies * 1000) / HZ;
+
+    // Get current time and calculate absolute timestamp
+    // Using boottime as reference to convert relative jiffies to absolute time
+    timestamp = ktime_get_real_seconds() - (jiffies - jiffies_value) / HZ;
+
+    // Convert to broken-down time
+    time64_to_tm(timestamp, 0, &tm_time);
+
+    // Format the time into the buffer with millisecond precision
+    return snprintf(buffer, buffer_len, "%04ld-%02d-%02d %02d:%02d:%02d.%03lu",
+                   tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
+                   tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, microseconds);
+}
+
 static int status_show(struct seq_file *s, void *p)
 {
        struct ceph_fs_client *fsc = s->private;
@@ -362,6 +402,122 @@ static int status_show(struct seq_file *s, void *p)
        return 0;
 }
 
+static int ceph_san_tls_show_internal(struct seq_file *s, void *p)
+{
+       struct ceph_san_tls_ctx *ctx;
+       struct ceph_san_log_iter iter;
+       struct ceph_san_log_entry *entry;
+       const struct ceph_san_source_info *source;
+       int total_entries = 0;
+       int total_contexts = 0;
+
+       /* Lock the logger to safely traverse the contexts list */
+       spin_lock(&g_logger.lock);
+
+       list_for_each_entry(ctx, &g_logger.contexts, list) {
+               /* Initialize iterator for this context's pagefrag */
+               ceph_san_log_iter_init(&iter, &ctx->pf);
+               int pid = ctx->pid;
+               char *comm = ctx->comm;
+               int ctx_entries = 0;
+
+               total_contexts++;
+
+               /* Lock the pagefrag before accessing entries */
+               spin_lock_bh(&ctx->pf.lock);
+
+               /* Iterate through log entries in this context */
+               while ((entry = ceph_san_log_iter_next(&iter)) != NULL) {
+                       char datetime_str[32];
+                       char reconstructed_msg[256];
+
+                       /* Validate entry before processing */
+                       if (!entry || !is_valid_kernel_addr(entry)) {
+                               seq_printf(s, "[%d][%s]: Invalid entry pointer %p\n", pid, comm, entry);
+                               break;
+                       }
+
+#if CEPH_SAN_DEBUG_POISON
+                       if (entry->debug_poison != CEPH_SAN_LOG_ENTRY_POISON) {
+                               seq_printf(s, "[%d][%s]: Corrupted log entry detected\n", pid, comm);
+                               continue;
+                       }
+#endif
+
+                       /* Calculate absolute timestamp from delta */
+                       unsigned long abs_jiffies = ctx->base_jiffies + entry->ts_delta;
+                       jiffies_to_formatted_time(abs_jiffies, datetime_str, sizeof(datetime_str));
+
+                       /* Get source information for this ID */
+                       source = ceph_san_get_source_info(entry->source_id);
+                       if (!source) {
+                               seq_printf(s, "[%d][%s][%s]:[Unknown Source ID %u]: %s\n",
+                                                 pid, comm, datetime_str, entry->source_id, entry->buffer);
+                               total_entries++;
+                               ctx_entries++;
+                               continue;
+                       }
+
+                       seq_printf(s, "[%d][%s][%s]:%s:%s:%u: ",
+                                         pid, comm, datetime_str,
+                                         source->file, source->func, source->line);
+
+                       int ret = ceph_san_log_reconstruct(entry, reconstructed_msg, sizeof(reconstructed_msg));
+                       if (ret >= 0)
+                               seq_puts(s, reconstructed_msg);
+                       else
+                               seq_printf(s, "<error reconstructing message: %d>", ret);
+                       seq_putc(s, '\n');
+
+                       total_entries++;
+                       ctx_entries++;
+               }
+
+               /* Unlock the pagefrag after we're done with this context */
+               spin_unlock_bh(&ctx->pf.lock);
+       }
+
+       spin_unlock(&g_logger.lock);
+
+       /* Add summary information */
+       seq_printf(s, "\n=== Summary ===\n");
+       seq_printf(s, "Total contexts: %d\n", total_contexts);
+       seq_printf(s, "Total entries: %d\n", total_entries);
+
+       return 0;
+}
+
+static int ceph_san_tls_show(struct seq_file *s, void *p)
+{
+       return ceph_san_tls_show_internal(s, p);
+}
+
+
+
+static int ceph_san_contexts_show(struct seq_file *s, void *p)
+{
+    struct ceph_san_tls_ctx *ctx;
+    unsigned long flags;
+
+    /* Lock the logger to safely traverse the contexts list */
+    spin_lock_irqsave(&g_logger.lock, flags);
+    seq_puts(s, "Active TLS Contexts:\n");
+    seq_puts(s, "PID      Command          State    Context ID    Base Jiffies\n");
+    seq_puts(s, "------------------------------------------------------------\n");
+    list_for_each_entry(ctx, &g_logger.contexts, list) {
+        char task_state = ctx->task ? task_state_to_char(ctx->task) : 'N';
+
+        seq_printf(s, "%-8d %-15s [%c] %-12llu %-12lu\n",
+                  ctx->pid,
+                  ctx->comm,
+                  task_state,
+                  ctx->id,
+                  ctx->base_jiffies);
+    }
+    spin_unlock_irqrestore(&g_logger.lock, flags);
+    return 0;
+}
+
 DEFINE_SHOW_ATTRIBUTE(mdsmap);
 DEFINE_SHOW_ATTRIBUTE(mdsc);
 DEFINE_SHOW_ATTRIBUTE(caps);
@@ -371,6 +527,8 @@ DEFINE_SHOW_ATTRIBUTE(metrics_file);
 DEFINE_SHOW_ATTRIBUTE(metrics_latency);
 DEFINE_SHOW_ATTRIBUTE(metrics_size);
 DEFINE_SHOW_ATTRIBUTE(metrics_caps);
+DEFINE_SHOW_ATTRIBUTE(ceph_san_tls);
+DEFINE_SHOW_ATTRIBUTE(ceph_san_contexts);
 
 
 /*
@@ -398,7 +556,7 @@ DEFINE_SIMPLE_ATTRIBUTE(congestion_kb_fops, congestion_kb_get,
 
 void ceph_fs_debugfs_cleanup(struct ceph_fs_client *fsc)
 {
-       boutc(fsc->client, "begin\n");
+       doutc(fsc->client, "begin\n");
        debugfs_remove(fsc->debugfs_bdi);
        debugfs_remove(fsc->debugfs_congestion_kb);
        debugfs_remove(fsc->debugfs_mdsmap);
@@ -406,15 +564,17 @@ void ceph_fs_debugfs_cleanup(struct ceph_fs_client *fsc)
        debugfs_remove(fsc->debugfs_caps);
        debugfs_remove(fsc->debugfs_status);
        debugfs_remove(fsc->debugfs_mdsc);
+       debugfs_remove(fsc->debugfs_cephsan_tls);
+       debugfs_remove(fsc->debugfs_cephsan_contexts);
        debugfs_remove_recursive(fsc->debugfs_metrics_dir);
-       boutc(fsc->client, "done\n");
+       doutc(fsc->client, "done\n");
 }
 
 void ceph_fs_debugfs_init(struct ceph_fs_client *fsc)
 {
        char name[NAME_MAX];
 
-       boutc(fsc->client, "begin\n");
+       doutc(fsc->client, "begin\n");
        fsc->debugfs_congestion_kb =
                debugfs_create_file("writeback_congestion_kb",
                                    0600,
@@ -459,6 +619,20 @@ void ceph_fs_debugfs_init(struct ceph_fs_client *fsc)
                                                  fsc,
                                                  &status_fops);
 
+
+       fsc->debugfs_cephsan_tls = debugfs_create_file("cephsan_tls",
+                                                       0444,
+                                                       fsc->client->debugfs_dir,
+                                                       fsc,
+                                                       &ceph_san_tls_fops);
+
+       /* Add the new contexts-only view */
+       fsc->debugfs_cephsan_contexts = debugfs_create_file("cephsan_contexts",
+                                                       0444,
+                                                       fsc->client->debugfs_dir,
+                                                       fsc,
+                                                       &ceph_san_contexts_fops);
+
        fsc->debugfs_metrics_dir = debugfs_create_dir("metrics",
                                                      fsc->client->debugfs_dir);
 
@@ -473,7 +647,6 @@ void ceph_fs_debugfs_init(struct ceph_fs_client *fsc)
        boutc(fsc->client, "done\n");
 }
 
-
 #else  /* CONFIG_DEBUG_FS */
 
 void ceph_fs_debugfs_init(struct ceph_fs_client *fsc)
@@ -484,4 +657,4 @@ void ceph_fs_debugfs_cleanup(struct ceph_fs_client *fsc)
 {
 }
 
-#endif  /* CONFIG_DEBUG_FS */
+#endif /* CONFIG_DEBUG_FS */
index acef7da8f425b7811accc23ce4edccd4adfc71ad..d980450c6f1a0a01a1c5bcb73a6dbb4aa923951e 100644 (file)
@@ -153,6 +153,8 @@ struct ceph_fs_client {
        struct dentry *debugfs_status;
        struct dentry *debugfs_mds_sessions;
        struct dentry *debugfs_metrics_dir;
+       struct dentry *debugfs_cephsan_tls;
+       struct dentry *debugfs_cephsan_contexts;
 #endif
 
 #ifdef CONFIG_CEPH_FSCACHE
index eb9d75957f6631c848b9b2f45aaea9ebc0c04f6f..79a4b5703adbcc25b9d26132478693ae19e9dddf 100644 (file)
@@ -3,6 +3,9 @@
 
 #include <linux/types.h> /* For size_t */
 
+/* Forward declaration */
+struct ceph_san_log_entry;
+
 /**
  * Reconstructs a formatted string from a buffer containing serialized values.
  * The function uses the format string to determine the types and number of values
  * @param size Size of the buffer in bytes
  * @param out Buffer to store the reconstructed string
  * @param out_size Size of the output buffer
- * @return Number of bytes written to out buffer, or -1 on error
+ * @return Number of bytes written to out buffer, or negative error code on failure
  */
 int ceph_san_des_reconstruct(const char *fmt, const void *buffer, size_t nr_args,
                            size_t size, char *out, size_t out_size);
 
+/**
+ * Reconstructs a formatted string from a log entry.
+ * This is a wrapper around ceph_san_des_reconstruct that handles log entry parsing.
+ *
+ * @param entry Log entry containing serialized data
+ * @param output Buffer to write the formatted string to
+ * @param output_size Size of the output buffer
+ * @return Length of formatted string, or negative error code on failure
+ */
+int ceph_san_log_reconstruct(const struct ceph_san_log_entry *entry, char *output, size_t output_size);
+
 #endif /* CEPH_SAN_DES_H */
index 22663a139fc6c9909adf1d8b1c5efebf890e3cc1..65acf95972c75a78b15c66212173f4b740310524 100644 (file)
@@ -115,8 +115,6 @@ void ceph_san_log_iter_init(struct ceph_san_log_iter *iter, struct cephsan_pagef
 /* Get next log entry, returns NULL when no more entries */
 struct ceph_san_log_entry *ceph_san_log_iter_next(struct ceph_san_log_iter *iter);
 
-/* Reconstruct a formatted string from a log entry */
-int ceph_san_log_reconstruct(const struct ceph_san_log_entry *entry, char *output, size_t output_size);
 
 /* Initialize the logging system */
 int ceph_san_logger_init(void);
index 8f714890cbf6cd6fc22e568705a91b0f82de4f21..365e31b5d2d57d083d05665b3e6d1a1d75e3bbc7 100644 (file)
@@ -42,7 +42,7 @@
     16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
 #define ceph_san_narg(...) ___ceph_san_narg(__VA_ARGS__)
 
-#define STR_MAX_SIZE 64
+#define STR_MAX_SIZE 256
 #define __sizeof(x) \
     (IS_STR(x) ? STR_MAX_SIZE : \
      (sizeof(x) < 4) ? 4 : sizeof(x))
@@ -130,7 +130,7 @@ static inline size_t strscpy_n(char *dst, const char *src)
        }
 
     dst[count] = '\0';
-    pr_err("strscpy_n: string truncated, exceeded max size %d\n", STR_MAX_SIZE);
+    pr_warn("strscpy_n: string truncated, exceeded max size %d\n", STR_MAX_SIZE);
 out:
        return count + 1;
 }
@@ -145,8 +145,18 @@ static inline ssize_t __strscpy(char *dst, const char *src)
 static inline void* strscpy_n_update(char *dst, const char *src, const char *file, int line)
 {
     ssize_t ret = __strscpy(dst, src);
-    if (!(unlikely(ret > 0 && ret < STR_MAX_SIZE))) {
-        panic("strscpy_n_update: ret = %zd at %s:%d :: %s < - %s\n", ret, file, line, dst, src);
+    if (unlikely(ret <= 0 || ret >= STR_MAX_SIZE)) {
+        pr_err("strscpy_n_update: string handling error ret=%zd at %s:%d :: dst='%s' src='%s'\n",
+               ret, file, line, dst, src ? src : "(null)");
+        /* Return safely instead of panicking - truncate and continue */
+        if (ret >= STR_MAX_SIZE) {
+            dst[STR_MAX_SIZE - 1] = '\0';
+            ret = STR_MAX_SIZE;
+        } else {
+            /* Handle null or empty string case */
+            dst[0] = '\0';
+            ret = 1;
+        }
     }
     return dst + round_up(ret, 4);
 }
index b37ae7c849c54fd883749574f59bad7f485443b0..b2aa5f0b180a8c2f180fc2a1120fc0534ed87c25 100644 (file)
 #include <linux/ceph/ceph_san_des.h>
+#include <linux/ceph/ceph_san_logger.h>  /* For log entry struct and source functions */
 #include <linux/string.h>   /* For strchr, strlen */
 #include <linux/ctype.h>    /* For isdigit */
 #include <linux/types.h>    /* For size_t */
 #include <linux/kernel.h>   /* For snprintf */
+#include <linux/printk.h>   /* For pr_err */
+#include <linux/align.h>    /* For round_up */
 
-static int parse_format_specifier(const char **fmt, char *spec) {
-    const char *p = *fmt;
-    char *s = spec;
 
-    /* Skip the '%' */
-    if (*p != '%') return -1;
-    *s++ = *p++;
+int ceph_san_des_reconstruct(const char *fmt, const void *buffer, size_t nr_args,
+                           size_t size, char *out, size_t out_size) {
+    const char *buf_start = (const char *)buffer;
+    const char *buf_ptr = buf_start;
+    const char *buf_end = buf_start + size;
+    const char *fmt_ptr = fmt;
+    char *out_ptr = out;
+    size_t remaining = out_size - 1; // Reserve space for null terminator
+    size_t arg_count = 0;
+    int ret;
 
-    /* Skip flags */
-    while (*p && (*p == '-' || *p == '+' || *p == ' ' || *p == '#' || *p == '0')) {
-        *s++ = *p++;
+    if (!fmt || !buffer || !out || !out_size) {
+        pr_err("ceph_san_des_reconstruct: invalid parameters\n");
+        return -EINVAL;
     }
 
-    /* Skip field width */
-    while (*p && isdigit(*p)) {
-        *s++ = *p++;
-    }
+    *out_ptr = '\0';
 
-    /* Skip precision */
-    if (*p == '.') {
-        *s++ = *p++;
-        while (*p && isdigit(*p)) {
-            *s++ = *p++;
+    // Process the format string
+    while (*fmt_ptr && remaining > 0) {
+        if (*fmt_ptr != '%') {
+            // Copy literal character
+            *out_ptr++ = *fmt_ptr++;
+            remaining--;
+            continue;
         }
-    }
 
-    /* Get length modifier */
-    if (*p == 'h' || *p == 'l' || *p == 'L' || *p == 'j' || *p == 'z' || *p == 't') {
-        *s++ = *p++;
-        if ((*p == 'h' || *p == 'l') && *(p-1) == *p) {
-            *s++ = *p++;
+        // Skip the '%'
+        fmt_ptr++;
+
+        // Handle %%
+        if (*fmt_ptr == '%') {
+            *out_ptr++ = '%';
+            fmt_ptr++;
+            remaining--;
+            continue;
         }
-    }
 
-    /* Get conversion specifier */
-    if (*p && strchr("diouxXeEfFgGaAcspn%", *p)) {
-        *s++ = *p++;
-    } else {
-        return -1;
-    }
+        // Skip flags (-+#0 space)
+        while (*fmt_ptr && (*fmt_ptr == '-' || *fmt_ptr == '+' || *fmt_ptr == '#' || 
+                           *fmt_ptr == '0' || *fmt_ptr == ' ')) {
+            fmt_ptr++;
+        }
 
-    *s = '\0';
-    *fmt = p;
-    return 0;
-}
+        // Skip field width (digits or *)
+        while (*fmt_ptr && (*fmt_ptr >= '0' && *fmt_ptr <= '9')) {
+            fmt_ptr++;
+        }
+        if (*fmt_ptr == '*') {
+            fmt_ptr++;
+        }
 
-int ceph_san_des_reconstruct(const char *fmt, const void *buffer, size_t nr_args,
-                           size_t size, char *out, size_t out_size) {
-    const unsigned char *buf = buffer;
-    const char *p = fmt;
-    char spec[32];
-    size_t offset = 0;
-    size_t out_offset = 0;
-    size_t arg_count = 0;
+        // Skip precision (.digits or .*)
+        if (*fmt_ptr == '.') {
+            fmt_ptr++;
+            while (*fmt_ptr && (*fmt_ptr >= '0' && *fmt_ptr <= '9')) {
+                fmt_ptr++;
+            }
+            if (*fmt_ptr == '*') {
+                fmt_ptr++;
+            }
+        }
 
-    if (!fmt || !buffer || !out || !out_size) {
-        return -1;
-    }
-    //printf("Starting reconstruction with buffer at %p, size %zu, nr_args %zu, out_size %zu\n",
-    //       buffer, size, nr_args, out_size);
-    while (*p && out_offset < out_size - 1) {
-        if (*p != '%') {
-            out[out_offset++] = *p++;
-            continue;
+        // Check argument count limit
+        if (arg_count >= nr_args) {
+            pr_err("ceph_san_des_reconstruct (%zu): too many format specifiers, expected %zu args. Format: '%.100s'\n", 
+                   arg_count, nr_args, fmt);
+            return -EINVAL;
         }
 
-        if (parse_format_specifier(&p, spec) < 0) {
-            return -1;
+        // Parse and handle format specifier
+        switch (*fmt_ptr) {
+        case 's': { // String (inline)
+            const char *str;
+            size_t str_len;
+            size_t max_scan_len;
+
+            // Validate we have enough buffer space for at least a null terminator
+            if (buf_ptr >= buf_end) {
+                pr_err("ceph_san_des_reconstruct (%zu): buffer overrun at string argument\n", arg_count);
+                return -EFAULT;
+            }
+
+            // String is stored inline in buffer
+            str = buf_ptr;
+
+            // Calculate maximum safe length to scan for null terminator
+            max_scan_len = buf_end - buf_ptr;
+
+            // Find string length with bounds checking
+            str_len = strnlen(str, max_scan_len);
+            if (str_len == max_scan_len && str[str_len - 1] != '\0') {
+                pr_err("ceph_san_des_reconstruct (%zu): unterminated string in buffer\n", arg_count);
+                return -EFAULT;
+            }
+
+            // Advance buffer pointer with proper alignment
+            buf_ptr += round_up(str_len + 1, 4);
+
+            // Check if buffer advance exceeds entry bounds
+            if (buf_ptr > buf_end) {
+                pr_err("ceph_san_des_reconstruct (%zu): string extends beyond buffer bounds\n", arg_count);
+                return -EFAULT;
+            }
+
+            // Copy string to output with bounds checking
+            if (str_len > remaining)
+                str_len = remaining;
+            memcpy(out_ptr, str, str_len);
+            out_ptr += str_len;
+            remaining -= str_len;
+            break;
         }
 
-        if (arg_count >= nr_args) {
-            return -1;
+        case 'd': case 'i': { // Integer
+            int val;
+
+            // Check buffer bounds before reading
+            if (buf_ptr + sizeof(int) > buf_end) {
+                pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading int\n", arg_count);
+                return -EFAULT;
+            }
+
+            val = *(int *)buf_ptr;
+            buf_ptr += sizeof(int);
+
+            ret = snprintf(out_ptr, remaining, "%d", val);
+            if (ret > 0) {
+                if (ret > remaining)
+                    ret = remaining;
+                out_ptr += ret;
+                remaining -= ret;
+            }
+            break;
         }
 
-        /* Check buffer overflow */
-        if (offset >= size) {
-            return -1;
+        case 'u': { // Unsigned integer
+            unsigned int val;
+
+            // Check buffer bounds before reading
+            if (buf_ptr + sizeof(unsigned int) > buf_end) {
+                pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading unsigned int\n", arg_count);
+                return -EFAULT;
+            }
+
+            val = *(unsigned int *)buf_ptr;
+            buf_ptr += sizeof(unsigned int);
+
+            ret = snprintf(out_ptr, remaining, "%u", val);
+            if (ret > 0) {
+                if (ret > remaining)
+                    ret = remaining;
+                out_ptr += ret;
+                remaining -= ret;
+            }
+            break;
         }
 
-        //printf("Processing specifier '%s' at offset %zu\n", spec, offset);
+        case 'x': { // Hex integer (lowercase)
+            unsigned int val;
 
-        /* Handle different format specifiers */
-        switch (spec[strlen(spec)-1]) {
-            case 'd':
-            case 'i':
-            case 'o':
-            case 'u':
-            case 'x':
-            case 'X': {
-                long long val;
-                const void *ptr = buf + offset;
-                if (strchr(spec, 'l')) {
-                    val = *(const long long*)ptr;
-                    offset += sizeof(long long);
-                } else {
-                    val = *(const int*)ptr;
-                    offset += sizeof(int);
-                }
-                //printf("Read integer value: %lld at address %p (offset %zu)\n", val, ptr, offset);
-                out_offset += snprintf(out + out_offset, out_size - out_offset, spec, val);
-                break;
+            // Check buffer bounds before reading
+            if (buf_ptr + sizeof(unsigned int) > buf_end) {
+                pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading unsigned int\n", arg_count);
+                return -EFAULT;
             }
 
-            case 'f':
-            case 'e':
-            case 'E':
-            case 'g':
-            case 'G':
-            case 'a':
-            case 'A': {
-                double val = *(const double*)(buf + offset);
-                offset += sizeof(double);
-                //printf("Read double value: %f at offset %zu\n", val, offset - sizeof(double));
-                out_offset += snprintf(out + out_offset, out_size - out_offset, spec, val);
-                break;
+            val = *(unsigned int *)buf_ptr;
+            buf_ptr += sizeof(unsigned int);
+
+            ret = snprintf(out_ptr, remaining, "%x", val);
+            if (ret > 0) {
+                if (ret > remaining)
+                    ret = remaining;
+                out_ptr += ret;
+                remaining -= ret;
             }
+            break;
+        }
 
-            case 'c': {
-                char val = *(const char*)(buf + offset);
-                offset += sizeof(char);
-                //printf("Read char value: %c at offset %zu\n", val, offset - sizeof(char));
-                out_offset += snprintf(out + out_offset, out_size - out_offset, spec, val);
-                break;
+        case 'X': { // Hex integer (uppercase)
+            unsigned int val;
+
+            // Check buffer bounds before reading
+            if (buf_ptr + sizeof(unsigned int) > buf_end) {
+                pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading unsigned int\n", arg_count);
+                return -EFAULT;
             }
 
-            case 's': {
-                const char *val = *(const char**)(buf + offset);
-                offset += sizeof(const char*);
-                //printf("Read string pointer: %p at offset %zu\n", val, offset - sizeof(const char*));
-                out_offset += snprintf(out + out_offset, out_size - out_offset, spec, val);
-                break;
+            val = *(unsigned int *)buf_ptr;
+            buf_ptr += sizeof(unsigned int);
+
+            ret = snprintf(out_ptr, remaining, "%X", val);
+            if (ret > 0) {
+                if (ret > remaining)
+                    ret = remaining;
+                out_ptr += ret;
+                remaining -= ret;
             }
+            break;
+        }
+
+        case 'o': { // Octal integer
+            unsigned int val;
 
-            case 'p': {
-                const void *val = *(const void**)(buf + offset);
-                offset += sizeof(const void*);
-                //printf("Read pointer value: %p at offset %zu\n", val, offset - sizeof(const void*));
-                out_offset += snprintf(out + out_offset, out_size - out_offset, spec, val);
-                break;
+            // Check buffer bounds before reading
+            if (buf_ptr + sizeof(unsigned int) > buf_end) {
+                pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading unsigned int\n", arg_count);
+                return -EFAULT;
             }
 
-            case '%': {
-                out[out_offset++] = '%';
-                break;
+            val = *(unsigned int *)buf_ptr;
+            buf_ptr += sizeof(unsigned int);
+
+            ret = snprintf(out_ptr, remaining, "%o", val);
+            if (ret > 0) {
+                if (ret > remaining)
+                    ret = remaining;
+                out_ptr += ret;
+                remaining -= ret;
+            }
+            break;
+        }
+
+        case 'p': { // Pointer
+            void *val;
+
+            // Check buffer bounds before reading
+            if (buf_ptr + sizeof(void *) > buf_end) {
+                pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading pointer\n", arg_count);
+                return -EFAULT;
+            }
+
+            val = *(void **)buf_ptr;
+            buf_ptr += sizeof(void *);
+
+            ret = snprintf(out_ptr, remaining, "%p", val);
+            if (ret > 0) {
+                if (ret > remaining)
+                    ret = remaining;
+                out_ptr += ret;
+                remaining -= ret;
+            }
+            break;
+        }
+
+        case 'l': { // Long types
+            fmt_ptr++; // Skip 'l'
+            if (*fmt_ptr == 'l') { // Long long
+                fmt_ptr++; // Skip second 'l'
+                if (*fmt_ptr == 'd' || *fmt_ptr == 'i') {
+                    long long val;
+                    if (buf_ptr + sizeof(long long) > buf_end) {
+                        pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading long long\n", arg_count);
+                        return -EFAULT;
+                    }
+                    val = *(long long *)buf_ptr;
+                    buf_ptr += sizeof(long long);
+                    ret = snprintf(out_ptr, remaining, "%lld", val);
+                } else if (*fmt_ptr == 'u') {
+                    unsigned long long val;
+                    if (buf_ptr + sizeof(unsigned long long) > buf_end) {
+                        pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading unsigned long long\n", arg_count);
+                        return -EFAULT;
+                    }
+                    val = *(unsigned long long *)buf_ptr;
+                    buf_ptr += sizeof(unsigned long long);
+                    ret = snprintf(out_ptr, remaining, "%llu", val);
+                } else if (*fmt_ptr == 'x') {
+                    unsigned long long val;
+                    if (buf_ptr + sizeof(unsigned long long) > buf_end) {
+                        pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading unsigned long long\n", arg_count);
+                        return -EFAULT;
+                    }
+                    val = *(unsigned long long *)buf_ptr;
+                    buf_ptr += sizeof(unsigned long long);
+                    ret = snprintf(out_ptr, remaining, "%llx", val);
+                } else if (*fmt_ptr == 'X') {
+                    unsigned long long val;
+                    if (buf_ptr + sizeof(unsigned long long) > buf_end) {
+                        pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading unsigned long long\n", arg_count);
+                        return -EFAULT;
+                    }
+                    val = *(unsigned long long *)buf_ptr;
+                    buf_ptr += sizeof(unsigned long long);
+                    ret = snprintf(out_ptr, remaining, "%llX", val);
+                } else if (*fmt_ptr == 'o') {
+                    unsigned long long val;
+                    if (buf_ptr + sizeof(unsigned long long) > buf_end) {
+                        pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading unsigned long long\n", arg_count);
+                        return -EFAULT;
+                    }
+                    val = *(unsigned long long *)buf_ptr;
+                    buf_ptr += sizeof(unsigned long long);
+                    ret = snprintf(out_ptr, remaining, "%llo", val);
+                } else {
+                    pr_err("ceph_san_des_reconstruct: invalid long long format specifier '%%ll%c'\n", *fmt_ptr);
+                    return -EINVAL;
+                }
+            } else { // Long
+                if (*fmt_ptr == 'd' || *fmt_ptr == 'i') {
+                    long val;
+                    if (buf_ptr + sizeof(long) > buf_end) {
+                        pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading long\n", arg_count);
+                        return -EFAULT;
+                    }
+                    val = *(long *)buf_ptr;
+                    buf_ptr += sizeof(long);
+                    ret = snprintf(out_ptr, remaining, "%ld", val);
+                } else if (*fmt_ptr == 'u') {
+                    unsigned long val;
+                    if (buf_ptr + sizeof(unsigned long) > buf_end) {
+                        pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading unsigned long\n", arg_count);
+                        return -EFAULT;
+                    }
+                    val = *(unsigned long *)buf_ptr;
+                    buf_ptr += sizeof(unsigned long);
+                    ret = snprintf(out_ptr, remaining, "%lu", val);
+                } else if (*fmt_ptr == 'x') {
+                    unsigned long val;
+                    if (buf_ptr + sizeof(unsigned long) > buf_end) {
+                        pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading unsigned long\n", arg_count);
+                        return -EFAULT;
+                    }
+                    val = *(unsigned long *)buf_ptr;
+                    buf_ptr += sizeof(unsigned long);
+                    ret = snprintf(out_ptr, remaining, "%lx", val);
+                } else if (*fmt_ptr == 'X') {
+                    unsigned long val;
+                    if (buf_ptr + sizeof(unsigned long) > buf_end) {
+                        pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading unsigned long\n", arg_count);
+                        return -EFAULT;
+                    }
+                    val = *(unsigned long *)buf_ptr;
+                    buf_ptr += sizeof(unsigned long);
+                    ret = snprintf(out_ptr, remaining, "%lX", val);
+                } else if (*fmt_ptr == 'o') {
+                    unsigned long val;
+                    if (buf_ptr + sizeof(unsigned long) > buf_end) {
+                        pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading unsigned long\n", arg_count);
+                        return -EFAULT;
+                    }
+                    val = *(unsigned long *)buf_ptr;
+                    buf_ptr += sizeof(unsigned long);
+                    ret = snprintf(out_ptr, remaining, "%lo", val);
+                } else {
+                    pr_err("ceph_san_des_reconstruct: invalid long format specifier '%%l%c'\n", *fmt_ptr);
+                    return -EINVAL;
+                }
             }
 
-            default:
-                return -1;
+            if (ret > 0) {
+                if (ret > remaining)
+                    ret = remaining;
+                out_ptr += ret;
+                remaining -= ret;
+            }
+            break;
+        }
+
+        case 'z': { // size_t
+            fmt_ptr++; // Skip 'z'
+            if (*fmt_ptr == 'u') {
+                size_t val;
+                if (buf_ptr + sizeof(size_t) > buf_end) {
+                    pr_err("ceph_san_des_reconstruct (%zu): buffer overrun reading size_t\n", arg_count);
+                    return -EFAULT;
+                }
+                val = *(size_t *)buf_ptr;
+                buf_ptr += sizeof(size_t);
+                ret = snprintf(out_ptr, remaining, "%zu", val);
+                if (ret > 0) {
+                    if (ret > remaining)
+                        ret = remaining;
+                    out_ptr += ret;
+                    remaining -= ret;
+                }
+            } else {
+                pr_err("ceph_san_des_reconstruct: invalid size_t format specifier '%%z%c'\n", *fmt_ptr);
+                return -EINVAL;
+            }
+            break;
+        }
+
+        default:
+            // Unknown format specifier
+            pr_err("ceph_san_des_reconstruct: unknown format specifier '%%%c'\n", *fmt_ptr);
+            return -EINVAL;
         }
 
+        fmt_ptr++;
         arg_count++;
     }
 
-    out[out_offset] = '\0';
-    return out_offset;
+    // Remove trailing newline if present
+    if (out_ptr > out && *(out_ptr - 1) == '\n') {
+        *(out_ptr - 1) = '\0';
+    } else {
+        *out_ptr = '\0';
+    }
+
+    return out_size - remaining - 1;
+}
+
+/**
+ * ceph_san_log_reconstruct - Reconstruct a formatted string from a log entry
+ * @entry: Log entry containing serialized data
+ * @output: Buffer to write the formatted string to
+ * @output_size: Size of the output buffer
+ *
+ * This is a wrapper around ceph_san_des_reconstruct that handles log entry parsing.
+ * It extracts the format string from the source info and calls the core reconstruction function.
+ *
+ * Returns length of formatted string, or negative error code on failure
+ */
+int ceph_san_log_reconstruct(const struct ceph_san_log_entry *entry, char *output, size_t output_size)
+{
+    const struct ceph_san_source_info *info;
+    const char *fmt;
+
+    if (!entry || !output || output_size == 0) {
+        pr_err("ceph_san_log_reconstruct: invalid parameters\n");
+        return -EINVAL;
+    }
+
+    /* Verify entry is a valid kernel address */
+    if (!is_valid_kernel_addr(entry)) {
+        pr_err("ceph_san_log_reconstruct: invalid entry pointer %p\n", entry);
+        return -EFAULT;
+    }
+
+    /* Verify entry buffer is a valid kernel address */
+    if (!is_valid_kernel_addr(entry->buffer)) {
+        pr_err("ceph_san_log_reconstruct: invalid buffer pointer %p for entry %p\n",
+               entry->buffer, entry);
+        return -EFAULT;
+    }
+
+#if CEPH_SAN_DEBUG_POISON
+    if (entry->debug_poison != CEPH_SAN_LOG_ENTRY_POISON) {
+        pr_err("ceph_san_log_reconstruct: corrupted log entry detected\n");
+        return -EFAULT;
+    }
+#endif
+
+    // Get format string from source info
+    info = ceph_san_get_source_info(entry->source_id);
+    if (!info) {
+        pr_err("ceph_san_log_reconstruct: source info not found for ID %u\n", entry->source_id);
+        return -EINVAL;
+    }
+
+    fmt = info->fmt;
+    if (!fmt) {
+        pr_err("ceph_san_log_reconstruct: format string not found in source info for ID %u\n", entry->source_id);
+        return -EINVAL;
+    }
+
+    // Count arguments in format string to pass to des_reconstruct
+    // This must match the parsing logic in ceph_san_des_reconstruct exactly
+    size_t nr_args = 0;
+    const char *p = fmt;
+    while (*p) {
+        if (*p == '%') {
+            p++; // Skip '%'
+            
+            // Handle %%
+            if (*p == '%') {
+                p++;
+                continue;
+            }
+            
+            // Skip flags (-+#0 space)
+            while (*p && (*p == '-' || *p == '+' || *p == '#' || 
+                         *p == '0' || *p == ' ')) {
+                p++;
+            }
+
+            // Skip field width (digits or *)
+            while (*p && (*p >= '0' && *p <= '9')) {
+                p++;
+            }
+            if (*p == '*') {
+                p++;
+            }
+
+            // Skip precision (.digits or .*)
+            if (*p == '.') {
+                p++;
+                while (*p && (*p >= '0' && *p <= '9')) {
+                    p++;
+                }
+                if (*p == '*') {
+                    p++;
+                }
+            }
+            
+            // Check if we have a valid conversion specifier
+            if (*p == 's' || *p == 'd' || *p == 'i' || *p == 'u' || *p == 'p' ||
+                *p == 'x' || *p == 'X' || *p == 'o' || *p == 'z') {
+                nr_args++;
+            } else if (*p == 'l') {
+                // Handle long types
+                p++; // Skip 'l'
+                if (*p == 'l') {
+                    p++; // Skip second 'l' for long long
+                }
+                // Now check the conversion specifier
+                if (*p == 'd' || *p == 'i' || *p == 'u' || *p == 'x' || 
+                    *p == 'X' || *p == 'o') {
+                    nr_args++;
+                }
+            }
+        }
+        if (*p) p++;
+    }
+
+    // Call the core reconstruction function
+    return ceph_san_des_reconstruct(fmt, entry->buffer, nr_args, entry->len, output, output_size);
 }
+EXPORT_SYMBOL(ceph_san_log_reconstruct);
index 7cea1eceb4de2a5c97b49a14ca853f783ebd2023..27873a0532474947c89da95b5f7ebb4e81861f72 100644 (file)
@@ -774,323 +774,6 @@ struct ceph_san_log_entry *ceph_san_log_iter_next(struct ceph_san_log_iter *iter
 }
 EXPORT_SYMBOL(ceph_san_log_iter_next);
 
-/**
- * ceph_san_log_reconstruct - Reconstruct a formatted string from a log entry
- * @entry: Log entry containing serialized data
- * @output: Buffer to write the formatted string to
- * @output_size: Size of the output buffer
- *
- * Returns length of formatted string, or negative error code on failure
- */
-int ceph_san_log_reconstruct(const struct ceph_san_log_entry *entry, char *output, size_t output_size)
-{
-    const struct ceph_san_source_info *info;
-    const char *fmt;
-    char *in_buffer, *out_ptr;
-    int ret;
-    int arg_count = 0;
-    size_t remaining = output_size - 1; // Reserve space for null terminator
-
-    if (!entry || !output || output_size == 0) {
-        pr_err("ceph_san_log_reconstruct: invalid parameters\n");
-        return -EINVAL;
-    }
-
-    /* Verify entry is a valid kernel address */
-    if (!is_valid_kernel_addr(entry)) {
-        pr_err("ceph_san_log_reconstruct: invalid entry pointer %p\n", entry);
-        return -EFAULT;
-    }
-    /* Dump entry buffer pointer and validate */
-    pr_debug("ceph_san_log_reconstruct: entry buffer pointer %llx (len %u) is %s\n",
-           (unsigned long long)entry->buffer, entry->len,
-           is_valid_kernel_addr(entry->buffer) ? "valid" : "invalid");
-    /* Verify entry buffer is a valid kernel address */
-    if (!is_valid_kernel_addr(entry->buffer)) {
-        pr_err("ceph_san_log_reconstruct: invalid buffer pointer %p for entry %p\n",
-               entry->buffer, entry);
-        return -EFAULT;
-    }
-
-    // Get format string from source info
-    info = ceph_san_get_source_info(entry->source_id);
-    if (!info) {
-        pr_err("ceph_san_log_reconstruct: source info not found for ID %u\n", entry->source_id);
-        return -EINVAL;
-    }
-
-    fmt = info->fmt;
-    if (!fmt) {
-        pr_err("ceph_san_log_reconstruct: format string not found in source info for ID %u\n", entry->source_id);
-        return -EINVAL;
-    }
-
-    in_buffer = (char *)(entry->buffer);
-    out_ptr = output;
-    *out_ptr = '\0';
-
-    // Process the format string
-    while (*fmt && remaining > 0) {
-        if (*fmt != '%') {
-            // Copy regular characters
-            *out_ptr++ = *fmt++;
-            remaining--;
-            continue;
-        }
-        arg_count++;
-        fmt++; // Skip the '%'
-        // Skip width specifiers
-        while (*fmt >= '0' && *fmt <= '9')
-            fmt++;
-
-        // Handle format specifiers
-        switch (*fmt) {
-        case '%': // Literal %
-            *out_ptr++ = '%';
-            remaining--;
-            break;
-        case 's': { // String
-            const char *str;
-            int type = 0;
-            // Inline string
-            str = in_buffer;
-            if (!is_valid_kernel_addr(str)) {
-                pr_err("ceph_san_log_reconstruct (%d): invalid inline string pointer %llx\n", arg_count, (unsigned long long)str);
-                return -EFAULT;
-            }
-            in_buffer += round_up(strlen(str) + 1, 4);
-            size_t len = strlen(str);
-            if (len > remaining)
-                len = remaining;
-            pr_debug("reconstruct: writing %s string '%s' (len=%zu) at out_offset=%ld\n",
-                   type ? "pointer" : "inline", str, strlen(str), in_buffer - entry->buffer);
-            memcpy(out_ptr, str, len);
-            out_ptr += len;
-            remaining -= len;
-            break;
-        }
-
-        case 'd': case 'i': { // Integer
-            int val = *(int *)in_buffer;
-            in_buffer += sizeof(int);
-            pr_debug("reconstruct: reading int %d at in_offset=%ld\n",
-                   val, in_buffer - entry->buffer);
-            ret = snprintf(out_ptr, remaining, "%d", val);
-            if (ret > 0) {
-                if (ret > remaining)
-                    ret = remaining;
-                out_ptr += ret;
-                remaining -= ret;
-            }
-            break;
-        }
-
-        case 'u': { // Unsigned integer
-            unsigned int val = *(unsigned int *)in_buffer;
-            in_buffer += sizeof(unsigned int);
-            pr_debug("reconstruct: reading int %u at in_offset=%ld\n",
-                   val, in_buffer - entry->buffer);
-            ret = snprintf(out_ptr, remaining, "%u", val);
-            if (ret > 0) {
-                if (ret > remaining)
-                    ret = remaining;
-                out_ptr += ret;
-                remaining -= ret;
-            }
-            break;
-        }
-
-        case 'x': case 'X': { // Hex
-            unsigned int val = *(unsigned int *)in_buffer;
-            in_buffer += sizeof(unsigned int);
-            pr_debug("reconstruct: reading int %u at in_offset=%ld\n",
-                   val, in_buffer - entry->buffer);
-            ret = snprintf(out_ptr, remaining, (*fmt == 'x') ? "%x" : "%X", val);
-            if (ret > 0) {
-                if (ret > remaining)
-                    ret = remaining;
-                out_ptr += ret;
-                remaining -= ret;
-            }
-            break;
-        }
-
-        case 'p': { // Pointer
-            void *val = *(void **)in_buffer;
-            in_buffer += sizeof(void *);
-            pr_debug("reconstruct: reading pointer %llx at in_offset=%ld\n",
-                   (unsigned long long)val, in_buffer - entry->buffer);
-            ret = snprintf(out_ptr, remaining, "%p", val);
-            if (ret > 0) {
-                if (ret > remaining)
-                    ret = remaining;
-                out_ptr += ret;
-                remaining -= ret;
-            }
-            break;
-        }
-
-        case 'o': { // Octal
-            unsigned int val = *(unsigned int *)in_buffer;
-            in_buffer += sizeof(unsigned int);
-            pr_debug("reconstruct: reading int %u (octal: %o) at in_offset=%ld\n",
-                   val, val, in_buffer - entry->buffer);
-            ret = snprintf(out_ptr, remaining, "%o", val);
-            if (ret > 0) {
-                if (ret > remaining)
-                    ret = remaining;
-                out_ptr += ret;
-                remaining -= ret;
-            }
-            break;
-        }
-
-        case 'l': { // Long or long long
-            fmt++;
-            if (*fmt == 'l') { // Long long
-                fmt++;
-                if (*fmt == 'd' || *fmt == 'i') {
-                    long long val = *(long long *)in_buffer;
-                    in_buffer += sizeof(long long);
-                    pr_debug("reconstruct: reading long long %lld at in_offset=%ld\n",
-                           val, in_buffer - entry->buffer);
-                    ret = snprintf(out_ptr, remaining, "%lld", val);
-                } else if (*fmt == 'u') {
-                    unsigned long long val = *(unsigned long long *)in_buffer;
-                    in_buffer += sizeof(unsigned long long);
-                    pr_debug("reconstruct: reading long long %llu at in_offset=%ld\n",
-                           val, in_buffer - entry->buffer);
-                    ret = snprintf(out_ptr, remaining, "%llu", val);
-                } else if (*fmt == 'x') {
-                    unsigned long long val = *(unsigned long long *)in_buffer;
-                    in_buffer += sizeof(unsigned long long);
-                    pr_debug("reconstruct: reading long long %llu at in_offset=%ld\n",
-                           val, in_buffer - entry->buffer);
-                    ret = snprintf(out_ptr, remaining, "%llx", val);
-                } else if (*fmt == 'X') {
-                    unsigned long long val = *(unsigned long long *)in_buffer;
-                    in_buffer += sizeof(unsigned long long);
-                    pr_debug("reconstruct: reading long long %llu at in_offset=%ld\n",
-                           val, in_buffer - entry->buffer);
-                    ret = snprintf(out_ptr, remaining, "%llX", val);
-                } else if (*fmt == 'o') {
-                    unsigned long long val = *(unsigned long long *)in_buffer;
-                    in_buffer += sizeof(unsigned long long);
-                    pr_debug("reconstruct: reading long long %llu (octal: %llo) at in_offset=%ld\n",
-                           val, val, in_buffer - entry->buffer);
-                    ret = snprintf(out_ptr, remaining, "%llo", val);
-                } else {
-                    pr_err("ceph_san_log_reconstruct: invalid long long format specifier '%%%c%c%c'\n", 'l', 'l', *fmt);
-                    return -EINVAL;
-                }
-            } else { // Long
-                if (*fmt == 'd' || *fmt == 'i') {
-                    long val = *(long *)in_buffer;
-                    in_buffer += sizeof(long);
-                    pr_debug("reconstruct: reading long %ld at in_offset=%ld\n",
-                           val, in_buffer - entry->buffer);
-                    ret = snprintf(out_ptr, remaining, "%ld", val);
-                } else if (*fmt == 'u') {
-                    unsigned long val = *(unsigned long *)in_buffer;
-                    in_buffer += sizeof(unsigned long);
-                    pr_debug("reconstruct: reading long %lu at in_offset=%ld\n",
-                           val, in_buffer - entry->buffer);
-                    ret = snprintf(out_ptr, remaining, "%lu", val);
-                } else if (*fmt == 'x') {
-                    unsigned long val = *(unsigned long *)in_buffer;
-                    in_buffer += sizeof(unsigned long);
-                    pr_debug("reconstruct: reading long %lx at in_offset=%ld\n",
-                           val, in_buffer - entry->buffer);
-                    ret = snprintf(out_ptr, remaining, "%lx", val);
-                } else if (*fmt == 'X') {
-                    unsigned long val = *(unsigned long *)in_buffer;
-                    in_buffer += sizeof(unsigned long);
-                    pr_debug("reconstruct: reading long %lx at in_offset=%ld\n",
-                           val, in_buffer - entry->buffer);
-                    ret = snprintf(out_ptr, remaining, "%lX", val);
-                } else if (*fmt == 'o') {
-                    unsigned long val = *(unsigned long *)in_buffer;
-                    in_buffer += sizeof(unsigned long);
-                    pr_debug("reconstruct: reading long %lu (octal: %lo) at in_offset=%ld\n",
-                           val, val, in_buffer - entry->buffer);
-                    ret = snprintf(out_ptr, remaining, "%lo", val);
-                } else {
-                    pr_err("ceph_san_log_reconstruct: invalid long format specifier '%%l%c'\n", *fmt);
-                    return -EINVAL;
-                }
-            }
-
-            if (ret > 0) {
-                if (ret > remaining)
-                    ret = remaining;
-                out_ptr += ret;
-                remaining -= ret;
-            }
-            break;
-        }
-
-        case 'z': {
-            fmt++; // Look at next character
-            if (*fmt == 'u' || *fmt == 'd') {
-                size_t val = *(size_t *)in_buffer;
-                in_buffer += sizeof(size_t);
-                pr_debug("reconstruct: reading size_t %zu at in_offset=%ld\n",
-                       val, in_buffer - entry->buffer);
-                ret = snprintf(out_ptr, remaining, (*fmt == 'u') ? "%zu" : "%zd", val);
-                if (ret > 0) {
-                    if (ret > remaining)
-                        ret = remaining;
-                    out_ptr += ret;
-                    remaining -= ret;
-                }
-            } else if (*fmt == 'x' || *fmt == 'X') {
-                size_t val = *(size_t *)in_buffer;
-                in_buffer += sizeof(size_t);
-                pr_debug("reconstruct: reading size_t %zx at in_offset=%ld\n",
-                       val, in_buffer - entry->buffer);
-                ret = snprintf(out_ptr, remaining, (*fmt == 'x') ? "%zx" : "%zX", val);
-                if (ret > 0) {
-                    if (ret > remaining)
-                        ret = remaining;
-                    out_ptr += ret;
-                    remaining -= ret;
-                }
-            } else if (*fmt == 'o') {
-                size_t val = *(size_t *)in_buffer;
-                in_buffer += sizeof(size_t);
-                pr_debug("reconstruct: reading size_t %zu (octal: %zo) at in_offset=%ld\n",
-                       val, val, in_buffer - entry->buffer);
-                ret = snprintf(out_ptr, remaining, "%zo", val);
-                if (ret > 0) {
-                    if (ret > remaining)
-                        ret = remaining;
-                    out_ptr += ret;
-                    remaining -= ret;
-                }
-            } else {
-                pr_err("ceph_san_log_reconstruct: invalid size_t format specifier '%%z%c'\n", *fmt);
-                return -EINVAL;
-            }
-            break;
-        }
-
-        default:
-            // Unknown format specifier
-            pr_debug("ceph_san_log_reconstruct: unknown format specifier '%%%c' in fmt string\n", *fmt);
-            return -EINVAL;
-        }
-
-        fmt++;
-    }
-    if (out_ptr > output && *(out_ptr - 1) == '\n') {
-        *(out_ptr - 1) = '\0';
-    } else {
-        *out_ptr = '\0';
-    }
-
-    return output_size - remaining - 1;
-}
-EXPORT_SYMBOL(ceph_san_log_reconstruct);
 
 /**
  * ceph_san_log_trim - Trim the current context's pagefrag by n bytes