Commit Diff


commit - b6e3f14ebd0601b1604dcb29fba07b6446a140b7
commit + 62b68cc461b5e298add3ab190fe9a38f3efefe7a
blob - 54dbd7facd349f637507abe774c6791f5e5f1c91
blob + a1edf799fc96acd3bcab63c318fd51e7b8fed377
--- src/bin/tpd.rs
+++ src/bin/tpd.rs
@@ -308,14 +308,34 @@ fn load_or_create_identity(path: &std::path::Path) -> 
     }
     let mut seed = [0u8; 32];
     tesseras_dht::sys::random_bytes(&mut seed);
-    if let Err(e) = std::fs::write(path, seed) {
-        log::warn!("identity: failed to save to {}: {e}", path.display());
-    } else {
-        log::info!("identity: generated new keypair at {}", path.display());
+    match write_private_file(path, &seed) {
+        Ok(()) => {
+            log::info!("identity: generated new keypair at {}", path.display());
+        }
+        Err(e) => {
+            log::warn!("identity: failed to save to {}: {e}", path.display());
+        }
     }
     seed.to_vec()
 }
 
+/// Write data to a file with mode 0600 (owner read/write only).
+fn write_private_file(
+    path: &std::path::Path,
+    data: &[u8],
+) -> std::io::Result<()> {
+    use std::io::Write;
+    use std::os::unix::fs::OpenOptionsExt;
+    let mut f = std::fs::OpenOptions::new()
+        .write(true)
+        .create(true)
+        .truncate(true)
+        .mode(0o600)
+        .open(path)?;
+    f.write_all(data)?;
+    f.sync_all()
+}
+
 const SIGINT: i32 = 2;
 const SIGTERM: i32 = 15;
 
blob - 88e3a09bea5287ba679394b265279790a557141d
blob + f12efd90c7433e2f60e65042de2b2693fe2f56e3
--- src/daemon.rs
+++ src/daemon.rs
@@ -413,8 +413,10 @@ fn handle_http(
     };
     let request = String::from_utf8_lossy(&buf[..n]);
 
-    // Parse "GET /<path> HTTP/1.x"
-    let path = match request.split_whitespace().nth(1) {
+    // Parse "METHOD /<path> HTTP/1.x"
+    let mut parts = request.split_whitespace();
+    let method = parts.next().unwrap_or("");
+    let path = match parts.next() {
         Some(p) => p,
         None => {
             http_response(&mut stream, 400, "text/plain", b"Bad Request");
@@ -422,8 +424,18 @@ fn handle_http(
         }
     };
 
+    if method != "GET" && method != "HEAD" {
+        http_response(&mut stream, 405, "text/plain", b"Method Not Allowed");
+        return;
+    }
+
     if path == "/" || path == "/favicon.ico" {
-        http_response(&mut stream, 200, "text/plain", b"tesseras-paste\n");
+        http_response(
+            &mut stream,
+            200,
+            "text/plain",
+            b"Hello Tesseras World\n",
+        );
         return;
     }
 
@@ -539,6 +551,7 @@ fn http_response(
         200 => "OK",
         400 => "Bad Request",
         403 => "Forbidden",
+        405 => "Method Not Allowed",
         404 => "Not Found",
         500 => "Internal Server Error",
         _ => "Unknown",
blob - 98c54817e574fabaf0b4650ad6da24831a411efc
blob + 04d74146365b24dc8c0554df5b11592bf282e117
--- src/store.rs
+++ src/store.rs
@@ -182,7 +182,8 @@ impl PasteStore {
 /// corruption if the process is killed mid-write.
 fn atomic_write(path: &Path, chunks: &[&[u8]]) -> std::io::Result<()> {
     let parent = path.parent().unwrap_or(Path::new("."));
-    let tmp = parent.join(format!(".tmp.{}", std::process::id()));
+    let name = path.file_name().and_then(|n| n.to_str()).unwrap_or("tmp");
+    let tmp = parent.join(format!(".tmp.{}.{}", std::process::id(), name));
     let mut f = fs::File::create(&tmp)?;
     for chunk in chunks {
         f.write_all(chunk)?;