Task 352: .LDB File Format

Task 352: .LDB File Format

1. List of properties of the .LDB file format intrinsic to its file system

The .LDB file is a Microsoft Access lock file created by the Jet database engine for shared databases. It is used to manage concurrent access by storing locking information to prevent multiple users from editing the same records simultaneously. The file is temporary, created when the database is opened in shared mode, and deleted when the last user closes it. It is located in the same directory as the corresponding .MDB or .ACCDB file and has the same base name (e.g., for database.mdb, the lock file is database.ldb).

The intrinsic properties (locking entries) are stored as a series of fixed-size records. Each record represents a user session and includes:

  • Computer Name: A 32-byte string (bytes 0-31 of each record), typically padded with spaces or nulls, identifying the machine accessing the database.
  • User Name (or Security Name): A 32-byte string (bytes 32-63 of each record), typically padded with spaces or nulls, identifying the logged-in user or security context.

The file consists of one or more 64-byte records (up to 255, for a maximum file size of 16 KB, supporting up to 255 concurrent users). Empty or unused records may be present, but active properties are the non-empty computer/user name pairs. The format is binary, with no header or footer—it's simply a concatenation of these records. The file system integration allows byte-range locking for record-level concurrency control.

I was unable to find publicly available direct download links for genuine .LDB files during my searches, as they are temporary system-generated lock files not commonly shared or archived online (they are deleted automatically when no users are accessing the database). Instead, you can easily create one by opening a shared Microsoft Access database (.MDB or .ACCDB) file on your system—the .LDB will appear in the same directory. If you need samples for testing, consider using a tool like LDBView.exe (downloadable from sites like access.mvps.org or similar Access resources) to view or simulate them.

3. Ghost blog embedded HTML JavaScript for drag-and-drop .LDB file dump

Here's an embedded HTML page with JavaScript that allows dragging and dropping a .LDB file. It reads the file as binary, parses it into 64-byte records, extracts the computer name and user name (trimming padding), and dumps the properties to the screen. You can embed this in a Ghost blog post as raw HTML.

.LDB File Properties Dumper

Drag and Drop .LDB File to Dump Properties

Drop .LDB file here

4. Python class for .LDB file handling

Here's a Python class that can open, decode/read, write, and print the properties from a .LDB file.

import os

class LDBHandler:
    RECORD_SIZE = 64
    FIELD_SIZE = 32

    def __init__(self, filename):
        self.filename = filename
        self.properties = []  # List of (computer_name, user_name) tuples

    def read(self):
        if not os.path.exists(self.filename):
            raise FileNotFoundError(f"{self.filename} not found.")
        with open(self.filename, 'rb') as f:
            data = f.read()
        self.properties = []
        for i in range(0, len(data), self.RECORD_SIZE):
            record = data[i:i + self.RECORD_SIZE]
            if len(record) < self.RECORD_SIZE:
                break
            computer_name = record[:self.FIELD_SIZE].decode('ascii', errors='ignore').rstrip('\x00 ').strip()
            user_name = record[self.FIELD_SIZE:].decode('ascii', errors='ignore').rstrip('\x00 ').strip()
            if computer_name or user_name:
                self.properties.append((computer_name, user_name))

    def print_properties(self):
        if not self.properties:
            print("No properties found.")
            return
        for idx, (comp, user) in enumerate(self.properties, 1):
            print(f"Entry {idx}: Computer Name = {comp}, User Name = {user}")

    def write(self, new_properties):
        # new_properties is a list of (computer_name, user_name) tuples
        with open(self.filename, 'wb') as f:
            for comp, user in new_properties:
                comp_bytes = comp.ljust(self.FIELD_SIZE, ' ')[:self.FIELD_SIZE].encode('ascii')
                user_bytes = user.ljust(self.FIELD_SIZE, ' ')[:self.FIELD_SIZE].encode('ascii')
                f.write(comp_bytes + user_bytes)

# Example usage:
# handler = LDBHandler('database.ldb')
# handler.read()
# handler.print_properties()
# handler.write([('PC1', 'Admin'), ('PC2', 'User')])

5. Java class for .LDB file handling

Here's a Java class that can open, decode/read, write, and print the properties from a .LDB file.

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public class LDBHandler {
    private static final int RECORD_SIZE = 64;
    private static final int FIELD_SIZE = 32;

    private String filename;
    private List<String[]> properties;  // Each array: [computer_name, user_name]

    public LDBHandler(String filename) {
        this.filename = filename;
        this.properties = new ArrayList<>();
    }

    public void read() throws IOException {
        File file = new File(filename);
        if (!file.exists()) {
            throw new FileNotFoundException(filename + " not found.");
        }
        try (FileInputStream fis = new FileInputStream(file)) {
            byte[] data = new byte[(int) file.length()];
            fis.read(data);
            properties.clear();
            for (int i = 0; i < data.length; i += RECORD_SIZE) {
                byte[] record = new byte[RECORD_SIZE];
                System.arraycopy(data, i, record, 0, RECORD_SIZE);
                String computerName = new String(record, 0, FIELD_SIZE, StandardCharsets.US_ASCII).trim().replaceAll("\0", "");
                String userName = new String(record, FIELD_SIZE, FIELD_SIZE, StandardCharsets.US_ASCII).trim().replaceAll("\0", "");
                if (!computerName.isEmpty() || !userName.isEmpty()) {
                    properties.add(new String[]{computerName, userName});
                }
            }
        }
    }

    public void printProperties() {
        if (properties.isEmpty()) {
            System.out.println("No properties found.");
            return;
        }
        for (int i = 0; i < properties.size(); i++) {
            String[] entry = properties.get(i);
            System.out.println("Entry " + (i + 1) + ": Computer Name = " + entry[0] + ", User Name = " + entry[1]);
        }
    }

    public void write(List<String[]> newProperties) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(filename)) {
            for (String[] entry : newProperties) {
                String comp = String.format("%-" + FIELD_SIZE + "s", entry[0]).substring(0, FIELD_SIZE);
                String user = String.format("%-" + FIELD_SIZE + "s", entry[1]).substring(0, FIELD_SIZE);
                fos.write(comp.getBytes(StandardCharsets.US_ASCII));
                fos.write(user.getBytes(StandardCharsets.US_ASCII));
            }
        }
    }

    // Example usage:
    // public static void main(String[] args) throws IOException {
    //     LDBHandler handler = new LDBHandler("database.ldb");
    //     handler.read();
    //     handler.printProperties();
    //     List<String[]> newProps = List.of(new String[]{"PC1", "Admin"}, new String[]{"PC2", "User"});
    //     handler.write(newProps);
    // }
}

6. JavaScript class for .LDB file handling

Here's a JavaScript class (Node.js compatible) that can open, decode/read, write, and print the properties from a .LDB file. It uses the 'fs' module for file I/O.

const fs = require('fs');

class LDBHandler {
    static RECORD_SIZE = 64;
    static FIELD_SIZE = 32;

    constructor(filename) {
        this.filename = filename;
        this.properties = [];  // Array of {computerName, userName} objects
    }

    read() {
        if (!fs.existsSync(this.filename)) {
            throw new Error(`${this.filename} not found.`);
        }
        const data = fs.readFileSync(this.filename);
        this.properties = [];
        for (let i = 0; i < data.length; i += LDBHandler.RECORD_SIZE) {
            const record = data.slice(i, i + LDBHandler.RECORD_SIZE);
            if (record.length < LDBHandler.RECORD_SIZE) break;
            const computerName = record.slice(0, LDBHandler.FIELD_SIZE).toString('ascii').replace(/\0/g, '').trim();
            const userName = record.slice(LDBHandler.FIELD_SIZE).toString('ascii').replace(/\0/g, '').trim();
            if (computerName || userName) {
                this.properties.push({ computerName, userName });
            }
        }
    }

    printProperties() {
        if (this.properties.length === 0) {
            console.log('No properties found.');
            return;
        }
        this.properties.forEach((entry, idx) => {
            console.log(`Entry ${idx + 1}: Computer Name = ${entry.computerName}, User Name = ${entry.userName}`);
        });
    }

    write(newProperties) {
        // newProperties is array of {computerName, userName} objects
        const buffer = Buffer.alloc(newProperties.length * LDBHandler.RECORD_SIZE);
        newProperties.forEach((entry, idx) => {
            const offset = idx * LDBHandler.RECORD_SIZE;
            buffer.write(entry.computerName.padEnd(LDBHandler.FIELD_SIZE, ' '), offset, LDBHandler.FIELD_SIZE, 'ascii');
            buffer.write(entry.userName.padEnd(LDBHandler.FIELD_SIZE, ' '), offset + LDBHandler.FIELD_SIZE, LDBHandler.FIELD_SIZE, 'ascii');
        });
        fs.writeFileSync(this.filename, buffer);
    }
}

// Example usage:
// const handler = new LDBHandler('database.ldb');
// handler.read();
// handler.printProperties();
// handler.write([{ computerName: 'PC1', userName: 'Admin' }, { computerName: 'PC2', userName: 'User' }]);

7. C class for .LDB file handling

Here's a C implementation (using structs for "class-like" behavior) that can open, decode/read, write, and print the properties from a .LDB file. Compile with a C compiler (e.g., gcc).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define RECORD_SIZE 64
#define FIELD_SIZE 32

typedef struct {
    char *filename;
    struct Entry *properties;
    int count;
} LDBHandler;

typedef struct Entry {
    char computerName[FIELD_SIZE + 1];
    char userName[FIELD_SIZE + 1];
} Entry;

LDBHandler* createLDBHandler(const char *filename) {
    LDBHandler *handler = (LDBHandler*)malloc(sizeof(LDBHandler));
    handler->filename = strdup(filename);
    handler->properties = NULL;
    handler->count = 0;
    return handler;
}

void destroyLDBHandler(LDBHandler *handler) {
    if (handler->properties) free(handler->properties);
    free(handler->filename);
    free(handler);
}

int readLDB(LDBHandler *handler) {
    FILE *file = fopen(handler->filename, "rb");
    if (!file) {
        perror("File not found");
        return -1;
    }
    fseek(file, 0, SEEK_END);
    long size = ftell(file);
    fseek(file, 0, SEEK_SET);
    char *data = (char*)malloc(size);
    fread(data, 1, size, file);
    fclose(file);

    int maxEntries = size / RECORD_SIZE;
    handler->properties = (Entry*)realloc(handler->properties, maxEntries * sizeof(Entry));
    handler->count = 0;
    for (int i = 0; i < size; i += RECORD_SIZE) {
        char record[RECORD_SIZE];
        memcpy(record, data + i, RECORD_SIZE);
        char comp[FIELD_SIZE + 1] = {0};
        char user[FIELD_SIZE + 1] = {0};
        strncpy(comp, record, FIELD_SIZE);
        strncpy(user, record + FIELD_SIZE, FIELD_SIZE);
        // Trim nulls and spaces
        comp[strcspn(comp, "\0 ")] = 0;
        user[strcspn(user, "\0 ")] = 0;
        if (strlen(comp) > 0 || strlen(user) > 0) {
            strcpy(handler->properties[handler->count].computerName, comp);
            strcpy(handler->properties[handler->count].userName, user);
            handler->count++;
        }
    }
    free(data);
    return 0;
}

void printProperties(LDBHandler *handler) {
    if (handler->count == 0) {
        printf("No properties found.\n");
        return;
    }
    for (int i = 0; i < handler->count; i++) {
        printf("Entry %d: Computer Name = %s, User Name = %s\n", i + 1, handler->properties[i].computerName, handler->properties[i].userName);
    }
}

int writeLDB(LDBHandler *handler, Entry *newProperties, int newCount) {
    FILE *file = fopen(handler->filename, "wb");
    if (!file) {
        perror("Cannot write file");
        return -1;
    }
    for (int i = 0; i < newCount; i++) {
        char record[RECORD_SIZE] = {0};
        strncpy(record, newProperties[i].computerName, FIELD_SIZE);
        strncpy(record + FIELD_SIZE, newProperties[i].userName, FIELD_SIZE);
        fwrite(record, 1, RECORD_SIZE, file);
    }
    fclose(file);
    return 0;
}

// Example usage:
// int main() {
//     LDBHandler *handler = createLDBHandler("database.ldb");
//     readLDB(handler);
//     printProperties(handler);
//     Entry newProps[2] = {{"PC1", "Admin"}, {"PC2", "User"}};
//     writeLDB(handler, newProps, 2);
//     destroyLDBHandler(handler);
//     return 0;
// }