ref: d8adc268f3dccd53e67adc2c7957309db795f6fe
parent: 338380c654e61a28d401869ec3ec30ad1cb85d4d
author: Jacob Moody <moody@posixcafe.org>
date: Sun Sep 21 14:16:54 EDT 2025
refactor
--- /dev/null
+++ b/cat.c
@@ -1,0 +1,36 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "s3.h"
+#include "cmd.h"
+
+_Noreturn void
+usage(void)
+{+ fprint(2, "Usage %s: s3://bucket/file\n", argv0);
+ exits("usage");+}
+
+void
+main(int argc , char **argv)
+{+ S3 s3;
+ int i;
+ Biobuf *b;
+ char path[512];
+
+ tmfmtinstall();
+ fmtinstall('H', encodefmt);+ i = parseargs(&s3, argc, argv);
+ argc -= i;
+ argv += i;
+
+ if(argc == 0)
+ usage();
+ if(parseuri(&s3, path, sizeof path, argv[0]) < 0)
+ usage();
+ b = Bfdopen(1, OWRITE);
+ download(&s3, path, b);
+ Bterm(b);
+ exits(nil);
+}
--- a/cmd.c
+++ b/cmd.c
@@ -1,128 +1,18 @@
#include <u.h>
#include <libc.h>
#include <bio.h>
-#include <mp.h>
-#include <libsec.h>
-#include <auth.h>
-#include "xml.h"
+#include "s3.h"
-typedef struct {- char *endpoint;
- char *host;
- char *access;
- char *bucket;
- char *region;
-} S3;
-
-typedef struct {- char method[16];
- char time[128];
- uchar payhash[SHA2_256dlen];
- char authhdr[512];
- char mime[32];
-} Hreq;
-
-static void
-datetime(char *date, int ndate, char *time, int ntime)
+void
+download(S3 *s3, char *path, Biobuf *local)
{- Tm t;
-
- tmnow(&t, nil);
- snprint(date, ndate, "%τ", tmfmt(&t, "YYYYMMDD"));
- snprint(time, ntime, "%sT%τZ", date, tmfmt(&t, "hhmmss"));
-}
-
-static void
-getkey(char *date, char *region, char *service, char *access, uchar out[SHA2_256dlen])
-{- int fd;
- AuthRpc *rpc;
- char buf[256];
- int n;
-
- fd = open("/mnt/factotum/rpc", ORDWR);- if(fd < 0)
- sysfatal("factotum rpc open: %r");- rpc = auth_allocrpc(fd);
- n = snprint(buf, sizeof buf, "proto=aws4 access=%s", access);
- if(auth_rpc(rpc, "start", buf, n) != ARok)
- sysfatal("auth_rpc: %r");- n = snprint(buf, sizeof buf, "%s %s %s", date, region, service);
- if(auth_rpc(rpc, "write", buf, n) != ARok)
- sysfatal("auth_rpc: %r");- if(auth_rpc(rpc, "read", nil, 0) != ARok)
- sysfatal("auth_rpc: %r");- if(rpc->narg != SHA2_256dlen)
- sysfatal("invalid auth_rpc output");- memcpy(out, rpc->arg, SHA2_256dlen);
- auth_freerpc(rpc);
- close(fd);
-}
-
-static void
-mkhreq(Hreq *hreq, S3 *s3, char *method, char *path)
-{- char date[64];
- uchar key[SHA2_256dlen], sig[SHA2_256dlen];
- char buf[512], req[512];
- char *sgndhdr;
-
- datetime(date, sizeof date, hreq->time, sizeof hreq->time);
- if(strcmp(method, "PUT") == 0){- snprint(buf, sizeof buf, "content-type:%s\nhost:%s\nx-amz-content-sha256:%.*lH\nx-amz-date:%s\n",
- hreq->mime, s3->host, SHA2_256dlen, hreq->payhash, hreq->time);
- sgndhdr = "content-type;host;x-amz-content-sha256;x-amz-date";
- } else if(strcmp(method, "GET") == 0 || strcmp(method, "DELETE")==0){- hreq->mime[0] = 0;
- sha2_256(nil, 0, hreq->payhash, nil);
- snprint(buf, sizeof buf, "host:%s\nx-amz-date:%s\n", s3->host, hreq->time);
- sgndhdr = "host;x-amz-date";
- } else
- sysfatal("invalid method");-
- snprint(req, sizeof req, "%s\n/%s/%s\n%s\n%s\n%s\n%.*lH",
- method, s3->bucket, path, "", buf, sgndhdr, SHA2_256dlen, hreq->payhash);
- sha2_256((uchar*)req, strlen(req), key, nil);
- snprint(buf, sizeof buf, "%s\n%s\n%s/%s/%s/aws4_request\n%.*lH",
- "AWS4-HMAC-SHA256", hreq->time, date, s3->region, "s3", SHA2_256dlen, key);
- getkey(date, s3->region, "s3", s3->access, key);
- hmac_sha2_256((uchar*)buf, strlen(buf), key, SHA2_256dlen, sig, nil);
-
- snprint(hreq->authhdr, sizeof hreq->authhdr, "%s Credential=%s/%s/%s/%s/aws4_request, SignedHeaders=%s, Signature=%.*lH",
- "AWS4-HMAC-SHA256", s3->access, date, s3->region, "s3", sgndhdr, SHA2_256dlen, sig);
- snprint(hreq->method, sizeof hreq->method, "%s", method);
-}
-
-static void
-prep(S3 *s3, int cfd, char *path, Hreq *hreq)
-{- if(fprint(cfd, "url %s/%s/%s", s3->endpoint, s3->bucket, path) < 0)
- sysfatal("url: %r");- if(fprint(cfd, "request %s", hreq->method) < 0)
- sysfatal("request: %r");- if(fprint(cfd, "headers Authorization:%s", hreq->authhdr) < 0)
- sysfatal("headers2: %r");- if(fprint(cfd, "headers x-amz-date:%s\nx-amz-content-sha256:%.*lH", hreq->time, SHA2_256dlen, hreq->payhash) < 0)
- sysfatal("headers: %r");- if(hreq->mime[0] != 0 && fprint(cfd, "contenttype %s", hreq->mime) < 0)
- sysfatal("contenttype: %r");-}
-
-static void
-download(S3 *s3, int cfd, char *conn, char *path, Biobuf *local)
-{int bfd;
long n;
- char buf[64];
char data[8192];
- Hreq hreq;
- mkhreq(&hreq, s3, "GET", path);
- prep(s3, cfd, path, &hreq);
- snprint(buf, sizeof buf, "/mnt/web/%s/body", conn);
- bfd = open(buf, OREAD);
+ bfd = s3get(s3, path);
if(bfd < 0)
- sysfatal("download body: %r");+ sysfatal("s3get: %r"); for(;;){n = read(bfd, data, sizeof data);
if(n < 0)
@@ -133,82 +23,7 @@
}
}
-static void
-mimetype(char *path, char *out, int nout)
-{- int p[2];
- char buf[256];
- int n;
-
- pipe(p);
- switch(fork()){- case 0:
- close(0);
- dup(p[0], 1);
- close(2);
- close(p[0]);
- close(p[1]);
- execl("/bin/file", "file", "-m", path, nil);- sysfatal("execl: %r");- default:
- close(p[0]);
- n = read(p[1], buf, sizeof buf - 1);
- if(n <= 0)
- sysfatal("no mimetype found");- buf[n - 1] = 0;
- snprint(out, nout, "%s", buf);
- }
-}
-
-static void
-upload(S3 *s3, int cfd, char *conn, char *localpath, char *remotepath)
-{- DigestState *ds;
- uchar data[8192];
- long n;
- int fd, bfd;
- char buf[256];
- Hreq hreq;
-
- mimetype(localpath, hreq.mime, sizeof hreq.mime);
- fd = open(localpath, OREAD);
- if(fd < 0)
- sysfatal("upload open: %r");- for(ds = nil;;){- n = read(fd, data, sizeof data);
- if(n < 0)
- sysfatal("file read: %r");- if(n == 0)
- break;
- ds = sha2_256(data, n, nil, ds);
- }
- sha2_256(nil, 0, hreq.payhash, ds);
- seek(fd, 0, 0);
-
- mkhreq(&hreq, s3, "PUT", remotepath);
- prep(s3, cfd, remotepath, &hreq);
- snprint(buf, sizeof buf, "/mnt/web/%s/postbody", conn);
- bfd = open(buf, OWRITE);
- if(bfd < 0)
- sysfatal("upload postbody open: %r");- for(;;){- n = read(fd, buf, sizeof buf);
- if(n < 0)
- sysfatal("file read: %r");- if(n == 0)
- break;
- if(write(bfd, buf, n) < 0)
- sysfatal("upload write: %r");- }
- close(bfd);
-
- snprint(buf, sizeof buf, "/mnt/web/%s/body", conn);
- if((bfd = open(buf, OREAD)) < 0)
- sysfatal("open3: %r");- close(bfd);
-}
-
-static int
+int
parseuri(S3 *s3, char *path, int npath, char *arg)
{char *p;
@@ -225,167 +40,41 @@
return 0;
}
-_Noreturn static void
-usage(void)
+int
+parseargs(S3 *s3, int argc, char **argv)
{- fprint(2, "Requires $AWS_ACCESS_KEY_ID and $AWS_ENDPOINT_URL_S3 defined\n");
- fprint(2, "Usage: %s cat s3://bucket/file\n", argv0);
- fprint(2, "Usage: %s cp source s3://bucket/destination\n", argv0);
- fprint(2, "Usage: %s cp s3://bucket/source <destination>\n", argv0);
- fprint(2, "Usage: %s rm s3://bucket/path\n", argv0);
- fprint(2, "Usage: %s ls s3://bucket/prefix\n", argv0);
- exits("usage");-}
+ extern _Noreturn void usage(void);
+ int initial;
-static void
-cp(S3 *s3, int cfd, char *conn, int argc, char **argv)
-{- char path[512];
- Biobuf *b;
- int fd;
-
- if(argc == 0 || argc > 2)
- usage();
- if(parseuri(s3, path, sizeof path, argv[0]) == 0){- if(argc > 1 && parseuri(s3, path, sizeof path, argv[1]) == 0)
- sysfatal("s3:// → s3:// is not implemented");- if(argc == 1)
- fd = 1;
- else {- fd = create(argv[1], OWRITE, 0644);
- if(fd < 0)
- sysfatal("create: %r");- }
- b = Bfdopen(fd, OWRITE);
- if(b == nil)
- sysfatal("Bfdopen: %r");- download(s3, cfd, conn, path, b);
- return;
- }
- if(argc == 1 || parseuri(s3, path, sizeof path, argv[1]) < 0)
- usage();
- upload(s3, cfd, conn, argv[0], path);
-}
-
-static void
-delete(S3 *s3, int cfd, char *conn, int argc, char **argv)
-{- int fd;
- char buf[256];
- Hreq hreq;
- char path[512];
-
- if(argc == 0)
- usage();
- if(parseuri(s3, path, sizeof path, argv[0]) < 0)
- usage();
- mkhreq(&hreq, s3, "DELETE", path);
- prep(s3, cfd, path, &hreq);
- snprint(buf, sizeof buf, "/mnt/web/%s/body", conn);
- fd = open(buf, OREAD);
- if(fd < 0)
- sysfatal("delete failed: %r");- close(fd);
-}
-
-static void
-list(S3 *s3, int cfd, char *conn, int argc, char **argv)
-{- int p[2];
- Biobuf *b[2];
- Xelem *x;
- char path[512];
-
- if(argc == 0)
- usage();
- if(parseuri(s3, path, sizeof path, argv[0]) < 0)
- usage();
- if(pipe(p) < 0)
- sysfatal("pipe: %r");- switch(fork()){- case -1:
- sysfatal("fork: %r");- case 0:
- close(p[1]);
- b[0] = Bfdopen(p[0], OWRITE);
- if(b[0] == nil)
- sysfatal("Bfdopen: %r");- download(s3, cfd, conn, path, b[0]);
- exits(nil);
- default:
- close(p[0]);
- break;
- }
- b[1] = Bfdopen(p[1], OREAD);
- if(b[1] == nil)
- sysfatal("Bfdopen: %r");- x = xmlread(b[1], 0);
- if(x == nil)
- sysfatal("file was not valid XML, maybe not a prefix?");- if((x = xmlget(x, "Contents", nil)) == nil)
- sysfatal("xml did not have Contents field");-
- for(; x != nil && xmlget(x, "Key", nil) != nil; x = x->next)
- print("%s\n", xmlget(x, "Key", nil)->v);-}
-
-struct {- char *cmd;
- void (*fn)(S3*,int,char*,int,char**);
-} cmdtab[] = {- { "cp", cp },- { "cat", cp },- { "rm", delete },- { "ls", list },-};
-
-void
-main(int argc , char **argv)
-{- S3 s3;
- int fd;
- int i;
- long n;
- char buf[64];
-
- tmfmtinstall();
- fmtinstall('H', encodefmt);+ initial = argc;
+ s3->access = s3->endpoint = s3->region = nil;
ARGBEGIN{- default:
- usage();
+ case 'a':
+ s3->access = strdup(EARGF(usage()));
break;
+ case 'u':
+ s3->endpoint = strdup(EARGF(usage()));
+ break;
+ case 'r':
+ s3->region = strdup(EARGF(usage()));
+ break;
}ARGEND
- if(argc == 0)
- usage();
- s3.access = getenv("AWS_ACCESS_KEY_ID");- s3.endpoint = getenv("AWS_ENDPOINT_URL_S3");- s3.region = getenv("AWS_DEFAULT_REGION");- if(s3.access == nil || s3.endpoint == nil)
+ if(s3->access == nil)
+ s3->access = getenv("AWS_ACCESS_KEY_ID");+ if(s3->endpoint == nil)
+ s3->endpoint = getenv("AWS_ENDPOINT_URL_S3");+ if(s3->region)
+ s3->region = getenv("AWS_DEFAULT_REGION");+
+ if(s3->access == nil || s3->endpoint == nil)
usage();
- if(s3.region == nil)
- s3.region = "auto";
+ if(s3->region == nil)
+ s3->region = strdup("auto");- s3.host = strstr(s3.endpoint, "://");
- if(s3.host == nil)
+ s3->host = strstr(s3->endpoint, "://");
+ if(s3->host == nil)
sysfatal("invalid endpoint url");- s3.host += 3;
-
- fd = open("/mnt/web/clone", ORDWR);- if(fd < 0)
- sysfatal("open: %r");- n = read(fd, buf, sizeof buf - 1);
- if(n <= 0)
- sysfatal("read: %r");- buf[n-1] = 0;
-
- for(i = 0; i < nelem(cmdtab); i++){- if(strcmp(argv[0], cmdtab[i].cmd) != 0)
- continue;
- argv++;
- argc--;
- cmdtab[i].fn(&s3, fd, buf, argc, argv);
- exits(nil);
- }
- sysfatal("unsupported cmd: %s", argv[0]);+ s3->host += 3;
+ return initial-argc;
}
--- /dev/null
+++ b/cmd.h
@@ -1,0 +1,3 @@
+void download(S3*, char*, Biobuf*);
+int parseuri(S3*, char*, int, char*);
+int parseargs(S3*, int, char**);
--- /dev/null
+++ b/cp.c
@@ -1,0 +1,119 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mp.h>
+#include <libsec.h>
+#include "s3.h"
+#include "cmd.h"
+
+static void
+mimetype(char *path, char *out, int nout)
+{+ int p[2];
+ char buf[256];
+ int n;
+
+ pipe(p);
+ switch(fork()){+ case 0:
+ close(0);
+ dup(p[0], 1);
+ close(2);
+ close(p[0]);
+ close(p[1]);
+ execl("/bin/file", "file", "-m", path, nil);+ sysfatal("execl: %r");+ default:
+ close(p[0]);
+ n = read(p[1], buf, sizeof buf - 1);
+ if(n <= 0)
+ sysfatal("no mimetype found");+ buf[n - 1] = 0;
+ snprint(out, nout, "%s", buf);
+ }
+}
+
+static void
+upload(S3 *s3, char *localpath, char *remotepath)
+{+ DigestState *ds;
+ uchar data[8192];
+ char mime[32];
+ uchar digest[SHA2_256dlen];
+ long n;
+ int fd, bfd;
+ char buf[256];
+
+ mimetype(localpath, mime, sizeof mime);
+ fd = open(localpath, OREAD);
+ if(fd < 0)
+ sysfatal("upload open: %r");+ for(ds = nil;;){+ n = read(fd, data, sizeof data);
+ if(n < 0)
+ sysfatal("file read: %r");+ if(n == 0)
+ break;
+ ds = sha2_256(data, n, nil, ds);
+ }
+ sha2_256(nil, 0, digest, ds);
+ seek(fd, 0, 0);
+
+ bfd = s3put(s3, remotepath, mime, digest);
+ if(bfd < 0)
+ sysfatal("upload postbody open: %r");+ for(;;){+ n = read(fd, buf, sizeof buf);
+ if(n < 0)
+ sysfatal("file read: %r");+ if(n == 0)
+ break;
+ if(write(bfd, buf, n) < 0)
+ sysfatal("upload write: %r");+ }
+ close(bfd);
+}
+
+_Noreturn void
+usage(void)
+{+ fprint(2, "Usage %s: source s3://bucket/dst\n", argv0);
+ fprint(2, "Usage %s: s3://bucket/source dst\n", argv0);
+ exits("usage");+}
+
+void
+main(int argc , char **argv)
+{+ S3 s3;
+ int i;
+ int fd;
+ Biobuf *b;
+ char path[512];
+
+ tmfmtinstall();
+ fmtinstall('H', encodefmt);+ i = parseargs(&s3, argc, argv);
+ argc -= i;
+ argv += i;
+
+ if(argc < 2)
+ usage();
+ if(parseuri(&s3, path, sizeof path, argv[0]) == 0){+ if(parseuri(&s3, path, sizeof path, argv[1]) == 0)
+ sysfatal("s3:// → s3:// is not implemented");+ fd = create(argv[1], OWRITE, 0644);
+ if(fd < 0)
+ sysfatal("create: %r");+ b = Bfdopen(fd, OWRITE);
+ if(b == nil)
+ sysfatal("Bfdopen: %r");+ download(&s3, path, b);
+ Bterm(b);
+ exits(nil);
+ }
+ if(parseuri(&s3, path, sizeof path, argv[1]) < 0)
+ usage();
+ upload(&s3, argv[0], path);
+ exits(nil);
+}
--- a/factotum.c
+++ b/factotum.c
@@ -462,7 +462,7 @@
close(fd);
}
-_Noreturn static void
+_Noreturn void
usage(void)
{fprint(2, "Usage: %s [-D] [-s srv] [-m mntpt]\n", argv0);
--- /dev/null
+++ b/ls.c
@@ -1,0 +1,63 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "s3.h"
+#include "cmd.h"
+#include "xml.h"
+
+_Noreturn void
+usage(void)
+{+ fprint(2, "Usage %s: s3://bucket/dir\n", argv0);
+ exits("usage");+}
+
+void
+main(int argc , char **argv)
+{+ S3 s3;
+ int i;
+ char path[512];
+ int p[2];
+ Biobuf *b[2];
+ Xelem *x;
+
+ tmfmtinstall();
+ fmtinstall('H', encodefmt);+ i = parseargs(&s3, argc, argv);
+ argc -= i;
+ argv += i;
+
+ if(argc == 0)
+ usage();
+ if(parseuri(&s3, path, sizeof path, argv[0]) < 0)
+ usage();
+ if(pipe(p) < 0)
+ sysfatal("pipe: %r");+ switch(fork()){+ case -1:
+ sysfatal("fork: %r");+ case 0:
+ close(p[1]);
+ b[0] = Bfdopen(p[0], OWRITE);
+ if(b[0] == nil)
+ sysfatal("Bfdopen: %r");+ download(&s3, path, b[0]);
+ exits(nil);
+ default:
+ close(p[0]);
+ break;
+ }
+ b[1] = Bfdopen(p[1], OREAD);
+ if(b[1] == nil)
+ sysfatal("Bfdopen: %r");+ x = xmlread(b[1], 0);
+ if(x == nil)
+ sysfatal("file was not valid XML, maybe not a prefix?");+ if((x = xmlget(x, "Contents", nil)) == nil)
+ sysfatal("xml did not have Contents field");+
+ for(; x != nil && xmlget(x, "Key", nil) != nil; x = x->next)
+ print("%s\n", xmlget(x, "Key", nil)->v);+ exits(nil);
+}
--- a/mkfile
+++ b/mkfile
@@ -1,15 +1,28 @@
</$objtype/mkfile
+path=/bin
BIN=$home/bin/$objtype/s3
TARG=\
- cmd\
factotum\
+ rm\
+ cat\
+ ls\
+ cp\
-OFILES=\
- xml.$O\
-
HFILES=\
xml.h\
</sys/src/cmd/mkmany
+
+$O.factotum: factotum.$O
+
+$O.cmd: xml.$O s3.$O cmd.$O
+
+$O.rm: rm.$O s3.$O cmd.$O
+
+$O.cat: cat.$O s3.$O cmd.$O
+
+$O.ls: ls.$O s3.$O cmd.$O xml.$O
+
+$O.cp: cp.$O s3.$O cmd.$O
--- /dev/null
+++ b/rm.c
@@ -1,0 +1,37 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include "s3.h"
+#include "cmd.h"
+
+_Noreturn void
+usage(void)
+{+ fprint(2, "Usage %s: s3://bucket/file\n", argv0);
+ exits("usage");+}
+
+void
+main(int argc , char **argv)
+{+ S3 s3;
+ int i;
+ int fd;
+ char path[512];
+
+ tmfmtinstall();
+ fmtinstall('H', encodefmt);+ i = parseargs(&s3, argc, argv);
+ argc -= i;
+ argv += i;
+
+ if(argc == 0)
+ usage();
+ if(parseuri(&s3, path, sizeof path, argv[0]) < 0)
+ usage();
+ fd = s3del(&s3, path);
+ if(fd < 0)
+ sysfatal("delete failed: %r");+ close(fd);
+ exits(nil);
+}
--- /dev/null
+++ b/s3.c
@@ -1,0 +1,185 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mp.h>
+#include <libsec.h>
+#include <auth.h>
+#include "s3.h"
+
+typedef struct {+ uchar *payhash;
+ char *mime;
+ char method[16];
+ char time[128];
+ char authhdr[512];
+} Hreq;
+
+static void
+datetime(char *date, int ndate, char *time, int ntime)
+{+ Tm t;
+
+ tmnow(&t, nil);
+ snprint(date, ndate, "%τ", tmfmt(&t, "YYYYMMDD"));
+ snprint(time, ntime, "%sT%τZ", date, tmfmt(&t, "hhmmss"));
+}
+
+static void
+getkey(char *date, char *region, char *service, char *access, uchar out[SHA2_256dlen])
+{+ int fd;
+ AuthRpc *rpc;
+ char buf[256];
+ int n;
+
+ fd = open("/mnt/factotum/rpc", ORDWR);+ if(fd < 0)
+ sysfatal("factotum rpc open: %r");+ rpc = auth_allocrpc(fd);
+ n = snprint(buf, sizeof buf, "proto=aws4 access=%s", access);
+ if(auth_rpc(rpc, "start", buf, n) != ARok)
+ sysfatal("auth_rpc: %r");+ n = snprint(buf, sizeof buf, "%s %s %s", date, region, service);
+ if(auth_rpc(rpc, "write", buf, n) != ARok)
+ sysfatal("auth_rpc: %r");+ if(auth_rpc(rpc, "read", nil, 0) != ARok)
+ sysfatal("auth_rpc: %r");+ if(rpc->narg != SHA2_256dlen)
+ sysfatal("invalid auth_rpc output");+ memcpy(out, rpc->arg, SHA2_256dlen);
+ auth_freerpc(rpc);
+ close(fd);
+}
+
+static void
+mkhreq(Hreq *hreq, S3 *s3, char *method, char *path)
+{+ char date[64];
+ uchar key[SHA2_256dlen], sig[SHA2_256dlen];
+ char buf[512], req[512];
+ char *sgndhdr;
+
+ datetime(date, sizeof date, hreq->time, sizeof hreq->time);
+ if(strcmp(method, "PUT") == 0){+ snprint(buf, sizeof buf, "content-type:%s\nhost:%s\nx-amz-content-sha256:%.*lH\nx-amz-date:%s\n",
+ hreq->mime, s3->host, SHA2_256dlen, hreq->payhash, hreq->time);
+ sgndhdr = "content-type;host;x-amz-content-sha256;x-amz-date";
+ } else if(strcmp(method, "GET") == 0 || strcmp(method, "DELETE")==0){+ hreq->mime = nil;
+ sha2_256(nil, 0, hreq->payhash, nil);
+ snprint(buf, sizeof buf, "host:%s\nx-amz-date:%s\n", s3->host, hreq->time);
+ sgndhdr = "host;x-amz-date";
+ } else
+ sysfatal("invalid method");+
+ snprint(req, sizeof req, "%s\n/%s/%s\n%s\n%s\n%s\n%.*lH",
+ method, s3->bucket, path, "", buf, sgndhdr, SHA2_256dlen, hreq->payhash);
+ sha2_256((uchar*)req, strlen(req), key, nil);
+ snprint(buf, sizeof buf, "%s\n%s\n%s/%s/%s/aws4_request\n%.*lH",
+ "AWS4-HMAC-SHA256", hreq->time, date, s3->region, "s3", SHA2_256dlen, key);
+ getkey(date, s3->region, "s3", s3->access, key);
+ hmac_sha2_256((uchar*)buf, strlen(buf), key, SHA2_256dlen, sig, nil);
+
+ snprint(hreq->authhdr, sizeof hreq->authhdr, "%s Credential=%s/%s/%s/%s/aws4_request, SignedHeaders=%s, Signature=%.*lH",
+ "AWS4-HMAC-SHA256", s3->access, date, s3->region, "s3", sgndhdr, SHA2_256dlen, sig);
+ snprint(hreq->method, sizeof hreq->method, "%s", method);
+}
+
+static int
+prep(S3 *s3, int cfd, char *path, Hreq *hreq)
+{+ if(fprint(cfd, "url %s/%s/%s", s3->endpoint, s3->bucket, path) < 0)
+ return -1;
+ if(fprint(cfd, "request %s", hreq->method) < 0)
+ return -1;
+ if(fprint(cfd, "headers Authorization:%s", hreq->authhdr) < 0)
+ return -1;
+ if(fprint(cfd, "headers x-amz-date:%s\nx-amz-content-sha256:%.*lH", hreq->time, SHA2_256dlen, hreq->payhash) < 0)
+ return -1;
+ if(hreq->mime != nil && fprint(cfd, "contenttype %s", hreq->mime) < 0)
+ return -1;
+ return 0;
+}
+
+static int
+wopen(char *buf, long n)
+{+ int fd;
+
+ fd = open("/mnt/web/clone", ORDWR);+ if(fd < 0)
+ return fd;
+ n = read(fd, buf, n - 1);
+ if(n <= 0){+ werrstr("short read from /mnt/web/clone");+ return -1;
+ }
+ buf[n-1] = 0;
+ return fd;
+}
+
+int
+s3get(S3 *s3, char *path)
+{+ char id[64];
+ char body[64];
+ int fd, bfd;
+ Hreq h;
+ uchar payhash[SHA2_256dlen];
+
+ h.payhash = payhash;
+ fd = wopen(id, sizeof id);
+ if(fd < 0)
+ return -1;
+ mkhreq(&h, s3, "GET", path);
+ if(prep(s3, fd, path, &h) < 0)
+ return -1;
+ snprint(body, sizeof body, "/mnt/web/%s/body", id);
+ bfd = open(body, OREAD);
+ close(fd);
+ return bfd;
+}
+
+int
+s3put(S3 *s3, char *path, char *mime, uchar *payhash)
+{+ char id[64];
+ char body[64];
+ int fd, bfd;
+ Hreq h;
+
+ h.mime = mime;
+ h.payhash = payhash;
+ fd = wopen(id, sizeof id);
+ if(fd < 0)
+ return -1;
+ mkhreq(&h, s3, "PUT", path);
+ if(prep(s3, fd, path, &h) < 0)
+ return -1;
+ snprint(body, sizeof body, "/mnt/web/%s/postbody", id);
+ bfd = open(body, OWRITE);
+ close(fd);
+ return bfd;
+}
+
+int
+s3del(S3 *s3, char *path)
+{+ char id[64];
+ char body[64];
+ int fd, bfd;
+ Hreq h;
+ uchar payhash[SHA2_256dlen];
+
+ h.payhash = payhash;
+ fd = wopen(id, sizeof id);
+ if(fd < 0)
+ return -1;
+ mkhreq(&h, s3, "DELETE", path);
+ if(prep(s3, fd, path, &h) < 0)
+ return -1;
+ snprint(body, sizeof body, "/mnt/web/%s/body", id);
+ bfd = open(body, OREAD);
+ close(fd);
+ return bfd;
+}
--- /dev/null
+++ b/s3.h
@@ -1,0 +1,11 @@
+typedef struct {+ char *endpoint;
+ char *host;
+ char *access;
+ char *bucket;
+ char *region;
+} S3;
+
+int s3get(S3 *s3, char *path);
+int s3del(S3 *s3, char *path);
+int s3put(S3 *s3, char *path, char *mime, uchar *payhash);
--
⑨