0
|
1 /* hfile_irods.c -- iRODS backend for low-level file streams.
|
|
2
|
|
3 Copyright (C) 2013, 2015 Genome Research Ltd.
|
|
4
|
|
5 Author: John Marshall <jm18@sanger.ac.uk>
|
|
6
|
|
7 Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8 of this software and associated documentation files (the "Software"), to deal
|
|
9 in the Software without restriction, including without limitation the rights
|
|
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11 copies of the Software, and to permit persons to whom the Software is
|
|
12 furnished to do so, subject to the following conditions:
|
|
13
|
|
14 The above copyright notice and this permission notice shall be included in
|
|
15 all copies or substantial portions of the Software.
|
|
16
|
|
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
20 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
22 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
23 DEALINGS IN THE SOFTWARE. */
|
|
24
|
|
25 #include <stdlib.h>
|
|
26 #include <string.h>
|
|
27 #include <errno.h>
|
|
28
|
|
29 #include "hfile_internal.h"
|
|
30
|
|
31 #include <rcConnect.h>
|
|
32 #include <dataObjOpen.h>
|
|
33 #include <dataObjRead.h>
|
|
34 #include <dataObjWrite.h>
|
|
35 #include <dataObjFsync.h>
|
|
36 #include <dataObjLseek.h>
|
|
37 #include <dataObjClose.h>
|
|
38
|
|
39 typedef struct {
|
|
40 hFILE base;
|
|
41 int descriptor;
|
|
42 } hFILE_irods;
|
|
43
|
|
44 static int status_errno(int status)
|
|
45 {
|
|
46 switch (status) {
|
|
47 case SYS_NO_API_PRIV: return EACCES;
|
|
48 case SYS_MALLOC_ERR: return ENOMEM;
|
|
49 case SYS_OUT_OF_FILE_DESC: return ENFILE;
|
|
50 case SYS_BAD_FILE_DESCRIPTOR: return EBADF;
|
|
51 case CAT_NO_ROWS_FOUND: return ENOENT;
|
|
52 case CATALOG_ALREADY_HAS_ITEM_BY_THAT_NAME: return EEXIST;
|
|
53 default: return EIO;
|
|
54 }
|
|
55 }
|
|
56
|
|
57 static void set_errno(int status)
|
|
58 {
|
|
59 int err = abs(status) % 1000;
|
|
60 errno = err? err : status_errno(status);
|
|
61 }
|
|
62
|
|
63 static struct {
|
|
64 rcComm_t *conn;
|
|
65 rodsEnv env;
|
|
66 } irods = { NULL };
|
|
67
|
|
68 static void irods_exit()
|
|
69 {
|
|
70 (void) rcDisconnect(irods.conn);
|
|
71 irods.conn = NULL;
|
|
72 }
|
|
73
|
|
74 static int irods_init()
|
|
75 {
|
|
76 rErrMsg_t err;
|
|
77 int ret;
|
|
78
|
|
79 ret = getRodsEnv(&irods.env);
|
|
80 if (ret < 0) goto error;
|
|
81
|
|
82 irods.conn = rcConnect(irods.env.rodsHost, irods.env.rodsPort,
|
|
83 irods.env.rodsUserName, irods.env.rodsZone,
|
|
84 NO_RECONN, &err);
|
|
85 if (irods.conn == NULL) { ret = err.status; goto error; }
|
|
86
|
|
87 if (strcmp(irods.env.rodsUserName, PUBLIC_USER_NAME) != 0) {
|
|
88 ret = clientLogin(irods.conn);
|
|
89 if (ret != 0) goto error;
|
|
90 }
|
|
91
|
|
92 // In the unlikely event atexit() fails, it's better to succeed here and
|
|
93 // carry on and do the I/O; then eventually when the program exits, we'll
|
|
94 // merely disconnect from the server uncleanly, as if we had aborted.
|
|
95 (void) atexit(irods_exit);
|
|
96
|
|
97 return 0;
|
|
98
|
|
99 error:
|
|
100 if (irods.conn) { (void) rcDisconnect(irods.conn); }
|
|
101 irods.conn = NULL;
|
|
102 set_errno(ret);
|
|
103 return -1;
|
|
104 }
|
|
105
|
|
106 static ssize_t irods_read(hFILE *fpv, void *buffer, size_t nbytes)
|
|
107 {
|
|
108 hFILE_irods *fp = (hFILE_irods *) fpv;
|
|
109 openedDataObjInp_t args;
|
|
110 bytesBuf_t buf;
|
|
111 int ret;
|
|
112
|
|
113 memset(&args, 0, sizeof args);
|
|
114 args.l1descInx = fp->descriptor;
|
|
115 args.len = nbytes;
|
|
116
|
|
117 buf.buf = buffer;
|
|
118 buf.len = nbytes;
|
|
119
|
|
120 ret = rcDataObjRead(irods.conn, &args, &buf);
|
|
121 if (ret < 0) set_errno(ret);
|
|
122 return ret;
|
|
123 }
|
|
124
|
|
125 static ssize_t irods_write(hFILE *fpv, const void *buffer, size_t nbytes)
|
|
126 {
|
|
127 hFILE_irods *fp = (hFILE_irods *) fpv;
|
|
128 openedDataObjInp_t args;
|
|
129 bytesBuf_t buf;
|
|
130 int ret;
|
|
131
|
|
132 memset(&args, 0, sizeof args);
|
|
133 args.l1descInx = fp->descriptor;
|
|
134 args.len = nbytes;
|
|
135
|
|
136 buf.buf = (void *) buffer; // ...the iRODS API is not const-correct here
|
|
137 buf.len = nbytes;
|
|
138
|
|
139 ret = rcDataObjWrite(irods.conn, &args, &buf);
|
|
140 if (ret < 0) set_errno(ret);
|
|
141 return ret;
|
|
142 }
|
|
143
|
|
144 static off_t irods_seek(hFILE *fpv, off_t offset, int whence)
|
|
145 {
|
|
146 hFILE_irods *fp = (hFILE_irods *) fpv;
|
|
147 openedDataObjInp_t args;
|
|
148 fileLseekOut_t *out = NULL;
|
|
149 int ret;
|
|
150
|
|
151 memset(&args, 0, sizeof args);
|
|
152 args.l1descInx = fp->descriptor;
|
|
153 args.offset = offset;
|
|
154 args.whence = whence;
|
|
155
|
|
156 ret = rcDataObjLseek(irods.conn, &args, &out);
|
|
157
|
|
158 if (out) { offset = out->offset; free(out); }
|
|
159 else offset = -1;
|
|
160 if (ret < 0) { set_errno(ret); return -1; }
|
|
161 return offset;
|
|
162 }
|
|
163
|
|
164 static int irods_flush(hFILE *fpv)
|
|
165 {
|
|
166 // FIXME rcDataObjFsync() doesn't seem to function as expected.
|
|
167 // For now, flush is a no-op: see https://github.com/samtools/htslib/issues/168
|
|
168 #if 0
|
|
169 hFILE_irods *fp = (hFILE_irods *) fpv;
|
|
170 openedDataObjInp_t args;
|
|
171 int ret;
|
|
172
|
|
173 memset(&args, 0, sizeof args);
|
|
174 args.l1descInx = fp->descriptor;
|
|
175
|
|
176 ret = rcDataObjFsync(irods.conn, &args);
|
|
177 if (ret < 0) set_errno(ret);
|
|
178 return ret;
|
|
179 #endif
|
|
180 return 0;
|
|
181 }
|
|
182
|
|
183 static int irods_close(hFILE *fpv)
|
|
184 {
|
|
185 hFILE_irods *fp = (hFILE_irods *) fpv;
|
|
186 openedDataObjInp_t args;
|
|
187 int ret;
|
|
188
|
|
189 memset(&args, 0, sizeof args);
|
|
190 args.l1descInx = fp->descriptor;
|
|
191
|
|
192 ret = rcDataObjClose(irods.conn, &args);
|
|
193 if (ret < 0) set_errno(ret);
|
|
194 return ret;
|
|
195 }
|
|
196
|
|
197 static const struct hFILE_backend irods_backend =
|
|
198 {
|
|
199 irods_read, irods_write, irods_seek, irods_flush, irods_close
|
|
200 };
|
|
201
|
|
202 hFILE *hopen_irods(const char *filename, const char *mode)
|
|
203 {
|
|
204 hFILE_irods *fp;
|
|
205 rodsPath_t path;
|
|
206 dataObjInp_t args;
|
|
207 int ret;
|
|
208
|
|
209 // Initialise the iRODS connection if this is the first use.
|
|
210 if (irods.conn == NULL) { if (irods_init() < 0) return NULL; }
|
|
211
|
|
212 if (strncmp(filename, "irods:", 6) == 0) filename += 6;
|
|
213 else { errno = EINVAL; return NULL; }
|
|
214
|
|
215 fp = (hFILE_irods *) hfile_init(sizeof (hFILE_irods), mode, 0);
|
|
216 if (fp == NULL) return NULL;
|
|
217
|
|
218 strncpy(path.inPath, filename, MAX_NAME_LEN-1);
|
|
219 path.inPath[MAX_NAME_LEN-1] = '\0';
|
|
220
|
|
221 ret = parseRodsPath(&path, &irods.env);
|
|
222 if (ret < 0) goto error;
|
|
223
|
|
224 memset(&args, 0, sizeof args);
|
|
225 strcpy(args.objPath, path.outPath);
|
|
226 args.openFlags = hfile_oflags(mode);
|
|
227 if (args.openFlags & O_CREAT) {
|
|
228 args.createMode = 0666;
|
|
229 addKeyVal(&args.condInput, DEST_RESC_NAME_KW,irods.env.rodsDefResource);
|
|
230 }
|
|
231
|
|
232 ret = rcDataObjOpen(irods.conn, &args);
|
|
233 if (ret < 0) goto error;
|
|
234 fp->descriptor = ret;
|
|
235
|
|
236 fp->base.backend = &irods_backend;
|
|
237 return &fp->base;
|
|
238
|
|
239 error:
|
|
240 hfile_destroy((hFILE *) fp);
|
|
241 set_errno(ret);
|
|
242 return NULL;
|
|
243 }
|