~kirkland/eucalyptus/label-metadata

« back to all changes in this revision

Viewing changes to clc/modules/storage-manager/src/edu/ucsb/eucalyptus/cloud/ws/Bukkit.java

  • Committer: graziano obertelli
  • Date: 2009-01-07 03:32:35 UTC
  • Revision ID: graziano@cs.ucsb.edu-20090107033235-oxhuexp18v8hg0pw
Tags: 1.4
from CVS

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Software License Agreement (BSD License)
 
3
 *
 
4
 * Copyright (c) 2008, Regents of the University of California
 
5
 * All rights reserved.
 
6
 *
 
7
 * Redistribution and use of this software in source and binary forms, with or
 
8
 * without modification, are permitted provided that the following conditions
 
9
 * are met:
 
10
 *
 
11
 * * Redistributions of source code must retain the above
 
12
 *   copyright notice, this list of conditions and the
 
13
 *   following disclaimer.
 
14
 *
 
15
 * * Redistributions in binary form must reproduce the above
 
16
 *   copyright notice, this list of conditions and the
 
17
 *   following disclaimer in the documentation and/or other
 
18
 *   materials provided with the distribution.
 
19
 *
 
20
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 
21
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 
22
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
23
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 
24
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 
25
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 
26
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 
27
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 
28
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
29
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 
30
 * POSSIBILITY OF SUCH DAMAGE.
 
31
 *
 
32
 * Author: Sunil Soman sunils@cs.ucsb.edu
 
33
 */
 
34
 
 
35
package edu.ucsb.eucalyptus.cloud.ws;
 
36
 
 
37
import edu.ucsb.eucalyptus.cloud.*;
 
38
import edu.ucsb.eucalyptus.cloud.entities.*;
 
39
import edu.ucsb.eucalyptus.keys.*;
 
40
import edu.ucsb.eucalyptus.msgs.*;
 
41
import edu.ucsb.eucalyptus.storage.StorageManager;
 
42
import edu.ucsb.eucalyptus.storage.fs.FileSystemStorageManager;
 
43
import edu.ucsb.eucalyptus.transport.query.WalrusQueryDispatcher;
 
44
import edu.ucsb.eucalyptus.util.*;
 
45
import org.apache.log4j.Logger;
 
46
import org.apache.tools.ant.util.DateUtils;
 
47
import org.apache.tools.tar.*;
 
48
 
 
49
import javax.crypto.*;
 
50
import javax.crypto.spec.*;
 
51
import java.io.*;
 
52
import java.nio.ByteBuffer;
 
53
import java.nio.channels.*;
 
54
import java.security.*;
 
55
import java.security.cert.X509Certificate;
 
56
import java.util.*;
 
57
import java.util.concurrent.LinkedBlockingQueue;
 
58
import java.util.zip.GZIPInputStream;
 
59
 
 
60
public class Bukkit {
 
61
 
 
62
    private static Logger LOG = Logger.getLogger( Bukkit.class );
 
63
 
 
64
    private static WalrusDataMessenger imageMessenger = new WalrusDataMessenger();
 
65
 
 
66
    static StorageManager storageManager;
 
67
    static boolean shouldEnforceUsageLimits = true;
 
68
 
 
69
    static {
 
70
        storageManager = new FileSystemStorageManager();
 
71
        String limits = System.getProperty(WalrusProperties.USAGE_LIMITS_PROPERTY);
 
72
        if(limits != null) {
 
73
            shouldEnforceUsageLimits = Boolean.parseBoolean(limits);
 
74
        }
 
75
    }
 
76
 
 
77
    //For unit testing
 
78
    public Bukkit () {}
 
79
 
 
80
    public CreateBucketResponseType CreateBucket(CreateBucketType request) throws EucalyptusCloudException {
 
81
        CreateBucketResponseType reply = (CreateBucketResponseType) request.getReply();
 
82
        String userId = request.getUserId();
 
83
 
 
84
        String bucketName = request.getBucket();
 
85
 
 
86
        if(userId == null) {
 
87
            throw new AccessDeniedException(bucketName);
 
88
        }
 
89
 
 
90
        AccessControlListType accessControlList = request.getAccessControlList();
 
91
        if (accessControlList == null) {
 
92
            accessControlList = new AccessControlListType();
 
93
        }
 
94
 
 
95
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
96
 
 
97
        if(shouldEnforceUsageLimits && !request.isAdministrator()) {
 
98
            BucketInfo searchBucket = new BucketInfo();
 
99
            searchBucket.setOwnerId(userId);
 
100
            List<BucketInfo> bucketList = db.query(searchBucket);
 
101
            if(bucketList.size() >= WalrusProperties.MAX_BUCKETS_PER_USER) {
 
102
                db.rollback();
 
103
                throw new TooManyBucketsException(bucketName);
 
104
            }
 
105
        }
 
106
 
 
107
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
108
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
109
 
 
110
        if(bucketList.size() > 0) {
 
111
            if(bucketList.get(0).getOwnerId().equals(userId)) {
 
112
                //bucket already exists and you created it
 
113
                db.rollback();
 
114
                throw new BucketAlreadyOwnedByYouException(bucketName);
 
115
            }
 
116
            //bucket already exists
 
117
            db.rollback();
 
118
            throw new BucketAlreadyExistsException(bucketName);
 
119
        }   else {
 
120
            //create bucket and set its acl
 
121
            BucketInfo bucket = new BucketInfo(userId, bucketName, new Date());
 
122
            ArrayList<GrantInfo> grantInfos = new ArrayList<GrantInfo>();
 
123
            bucket.addGrants(userId, grantInfos, accessControlList);
 
124
            bucket.setGrants(grantInfos);
 
125
            bucket.setBucketSize(0L);
 
126
 
 
127
            ArrayList<ObjectInfo> objectInfos = new ArrayList<ObjectInfo>();
 
128
            bucket.setObjects(objectInfos);
 
129
            //call the storage manager to save the bucket to disk
 
130
            try {
 
131
                storageManager.createBucket(bucketName);
 
132
                db.add(bucket);
 
133
            } catch (IOException ex) {
 
134
                LOG.warn(ex, ex);
 
135
                //TODO: set exception code in reply
 
136
            }
 
137
        }
 
138
        db.commit();
 
139
 
 
140
        reply.setBucket(bucketName);
 
141
        return reply;
 
142
    }
 
143
 
 
144
    public DeleteBucketResponseType DeleteBucket(DeleteBucketType request) throws EucalyptusCloudException {
 
145
        DeleteBucketResponseType reply = (DeleteBucketResponseType) request.getReply();
 
146
 
 
147
        String bucketName = request.getBucket();
 
148
 
 
149
        String userId = request.getUserId();
 
150
 
 
151
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
152
        BucketInfo searchBucket = new BucketInfo(bucketName);
 
153
        List<BucketInfo> bucketList = db.query(searchBucket);
 
154
 
 
155
 
 
156
        if(bucketList.size() > 0) {
 
157
            BucketInfo bucketFound = bucketList.get(0);
 
158
            if (bucketFound.canWrite(userId)) {
 
159
                List<ObjectInfo> objectInfos = bucketFound.getObjects();
 
160
                if(objectInfos.size() == 0) {
 
161
                    db.delete(bucketFound);
 
162
                    //Actually remove the bucket from the backing store
 
163
                    try {
 
164
                        storageManager.deleteBucket(bucketName);
 
165
                    } catch (IOException ex) {
 
166
                        //set exception code in reply
 
167
                    }
 
168
                    Status status = new Status();
 
169
                    status.setCode(204);
 
170
                    status.setDescription("No Content");
 
171
                    reply.setStatus(status);
 
172
                } else {
 
173
                    db.rollback();
 
174
                    throw new BucketNotEmptyException(bucketName);
 
175
                }
 
176
            } else {
 
177
                db.rollback();
 
178
                throw new AccessDeniedException(bucketName);
 
179
            }
 
180
        } else {
 
181
            db.rollback();
 
182
            throw new NoSuchBucketException(bucketName);
 
183
        }
 
184
        db.commit();
 
185
        return reply;
 
186
    }
 
187
 
 
188
    public ListAllMyBucketsResponseType ListAllMyBuckets(ListAllMyBucketsType request) throws EucalyptusCloudException {
 
189
        ListAllMyBucketsResponseType reply = (ListAllMyBucketsResponseType) request.getReply();
 
190
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
191
        String userId = request.getUserId();
 
192
 
 
193
        if(userId == null) {
 
194
            throw new AccessDeniedException("no such user");
 
195
        }
 
196
 
 
197
        EntityWrapper<UserInfo> db2 = new EntityWrapper<UserInfo>();
 
198
        UserInfo searchUser = new UserInfo(userId);
 
199
        List<UserInfo> userInfoList = db2.query(searchUser);
 
200
 
 
201
        if(userInfoList.size() > 0) {
 
202
            UserInfo user = userInfoList.get(0);
 
203
            BucketInfo searchBucket = new BucketInfo();
 
204
            searchBucket.setOwnerId(userId);
 
205
            List<BucketInfo> bucketInfoList = db.query(searchBucket);
 
206
 
 
207
            ArrayList<BucketListEntry> buckets = new ArrayList<BucketListEntry>();
 
208
 
 
209
            for(BucketInfo bucketInfo: bucketInfoList) {
 
210
                buckets.add(new BucketListEntry(bucketInfo.getBucketName(), DateUtils.format(bucketInfo.getCreationDate().getTime(), DateUtils.ISO8601_DATETIME_PATTERN) + ".000Z"));
 
211
            }
 
212
 
 
213
            CanonicalUserType owner = new CanonicalUserType(user.getQueryId(), user.getUserName());
 
214
            ListAllMyBucketsList bucketList = new ListAllMyBucketsList();
 
215
            reply.setOwner(owner);
 
216
            bucketList.setBuckets(buckets);
 
217
            reply.setBucketList(bucketList);
 
218
        } else {
 
219
            db.rollback();
 
220
            throw new AccessDeniedException(userId);
 
221
        }
 
222
        db.commit();
 
223
        return reply;
 
224
    }
 
225
 
 
226
    public GetBucketAccessControlPolicyResponseType GetBucketAccessControlPolicy(GetBucketAccessControlPolicyType request) throws EucalyptusCloudException
 
227
    {
 
228
        GetBucketAccessControlPolicyResponseType reply = (GetBucketAccessControlPolicyResponseType) request.getReply();
 
229
 
 
230
        String bucketName = request.getBucket();
 
231
        String userId = request.getUserId();
 
232
        String ownerId = null;
 
233
 
 
234
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
235
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
236
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
237
 
 
238
        AccessControlListType accessControlList = new AccessControlListType();
 
239
 
 
240
        if (bucketList.size() > 0) {
 
241
            //construct access control policy from grant infos
 
242
            BucketInfo bucket = bucketList.get(0);
 
243
            List<GrantInfo> grantInfos = bucket.getGrants();
 
244
            if (bucket.canReadACP(userId)) {
 
245
                ownerId = bucket.getOwnerId();
 
246
                ArrayList<Grant> grants = new ArrayList<Grant>();
 
247
                for (GrantInfo grantInfo: grantInfos) {
 
248
                    String uId = grantInfo.getUserId();
 
249
                    UserInfo userInfo = new UserInfo(uId);
 
250
                    EntityWrapper<UserInfo> db2 = new EntityWrapper<UserInfo>();
 
251
                    List<UserInfo> grantUserInfos = db2.query(userInfo);
 
252
                    db2.commit();
 
253
 
 
254
                    if(grantUserInfos.size() > 0) {
 
255
                        UserInfo grantUserInfo = grantUserInfos.get(0);
 
256
                        addPermission(grants, grantUserInfo, grantInfo);
 
257
                    }
 
258
                }
 
259
                accessControlList.setGrants(grants);
 
260
            }
 
261
        }   else {
 
262
            db.rollback();
 
263
            throw new NoSuchBucketException(bucketName);
 
264
        }
 
265
        UserInfo userInfo = new UserInfo(ownerId);
 
266
        EntityWrapper<UserInfo> db2 = new EntityWrapper<UserInfo>();
 
267
        List<UserInfo> ownerUserInfos = db2.query(userInfo);
 
268
        db2.commit();
 
269
 
 
270
        AccessControlPolicyType accessControlPolicy = new AccessControlPolicyType();
 
271
        if(ownerUserInfos.size() > 0) {
 
272
            UserInfo ownerUserInfo = ownerUserInfos.get(0);
 
273
            accessControlPolicy.setOwner(new CanonicalUserType(ownerUserInfo.getQueryId(), ownerUserInfo.getUserName()));
 
274
            accessControlPolicy.setAccessControlList(accessControlList);
 
275
        }
 
276
        reply.setAccessControlPolicy(accessControlPolicy);
 
277
        db.commit();
 
278
        return reply;
 
279
    }
 
280
 
 
281
 
 
282
    private static void addPermission(ArrayList<Grant>grants, UserInfo userInfo, GrantInfo grantInfo) {
 
283
        CanonicalUserType user = new CanonicalUserType(userInfo.getQueryId(), userInfo.getUserName());
 
284
 
 
285
        if (grantInfo.isRead() && grantInfo.isWrite() && grantInfo.isReadACP() && grantInfo.isWriteACP()) {
 
286
            grants.add(new Grant(user, "FULL_CONTROL"));
 
287
            return;
 
288
        }
 
289
 
 
290
        if (grantInfo.isRead()) {
 
291
            grants.add(new Grant(user, "READ"));
 
292
        }
 
293
 
 
294
        if (grantInfo.isWrite()) {
 
295
            grants.add(new Grant(user, "WRITE"));
 
296
        }
 
297
 
 
298
        if (grantInfo.isReadACP()) {
 
299
            grants.add(new Grant(user, "READ_ACP"));
 
300
        }
 
301
 
 
302
        if (grantInfo.isWriteACP()) {
 
303
            grants.add(new Grant(user, "WRITE_ACP"));
 
304
        }
 
305
    }
 
306
 
 
307
    public PutObjectResponseType PutObject (PutObjectType request) throws EucalyptusCloudException {
 
308
        PutObjectResponseType reply = (PutObjectResponseType) request.getReply();
 
309
        String userId = request.getUserId();
 
310
 
 
311
        String bucketName = request.getBucket();
 
312
        String objectName = request.getKey();
 
313
 
 
314
        Long oldBucketSize = 0L;
 
315
 
 
316
        String md5 = "";
 
317
        Date lastModified = null;
 
318
 
 
319
        AccessControlListType accessControlList = request.getAccessControlList();
 
320
        if (accessControlList == null) {
 
321
            accessControlList = new AccessControlListType();
 
322
        }
 
323
 
 
324
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
325
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
326
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
327
 
 
328
        if(bucketList.size() > 0) {
 
329
            BucketInfo bucket = bucketList.get(0);
 
330
            if (bucket.canWrite(userId)) {
 
331
 
 
332
                ObjectInfo foundObject = null;
 
333
                List<ObjectInfo> objectInfos = bucket.getObjects();
 
334
                for (ObjectInfo objectInfo: objectInfos) {
 
335
                    if (objectInfo.getObjectName().equals(objectName)) {
 
336
                        //key (object) exists. check perms
 
337
                        if (!objectInfo.canWrite(userId)) {
 
338
                            db.rollback();
 
339
                            throw new AccessDeniedException(objectName);
 
340
                        }
 
341
                        foundObject = objectInfo;
 
342
                        oldBucketSize = -foundObject.getSize();
 
343
                        break;
 
344
                    }
 
345
                }
 
346
                //write object to bucket
 
347
                if (foundObject == null) {
 
348
                    //not found. create an object info
 
349
                    foundObject = new ObjectInfo(objectName);
 
350
                    List<GrantInfo> grantInfos = new ArrayList<GrantInfo>();
 
351
                    foundObject.addGrants(userId, grantInfos, accessControlList);
 
352
                    foundObject.setGrants(grantInfos);
 
353
                    objectInfos.add(foundObject);
 
354
                } else {
 
355
                    //object already exists. see if we can modify acl
 
356
                    if (foundObject.canWriteACP(userId)) {
 
357
                        List<GrantInfo> grantInfos = foundObject.getGrants();
 
358
                        foundObject.addGrants(userId, grantInfos, accessControlList);
 
359
                    }
 
360
                }
 
361
                foundObject.setObjectName(objectName);
 
362
                foundObject.setOwnerId(userId);
 
363
                foundObject.addMetaData(request.getMetaData());
 
364
                //writes are unconditional
 
365
                String randomKey = request.getRandomKey();
 
366
 
 
367
                WalrusDataMessenger messenger = WalrusQueryDispatcher.getWriteMessenger();
 
368
                String key = bucketName + "." + objectName;
 
369
                LinkedBlockingQueue<WalrusDataMessage> putQueue = messenger.getQueue(key, randomKey);
 
370
 
 
371
                try {
 
372
                    WalrusDataMessage dataMessage = null;
 
373
                    String tempObjectName = objectName;
 
374
                    MessageDigest digest = null;
 
375
                    long size = 0;
 
376
                    int offset = 0;
 
377
                    while ((dataMessage = putQueue.take())!=null) {
 
378
                        if(WalrusDataMessage.isStart(dataMessage)) {
 
379
                            tempObjectName = objectName + "." + Hashes.getRandom(12);
 
380
                            digest = Hashes.Digest.MD5.get();
 
381
                        } else if(WalrusDataMessage.isEOF(dataMessage)) {
 
382
                            //commit object
 
383
                            try {
 
384
                                storageManager.renameObject(bucketName, tempObjectName, objectName);
 
385
                            } catch (IOException ex) {
 
386
                                //TODO: error handling
 
387
                                LOG.warn(ex, ex);
 
388
                            }
 
389
                            md5 = bytesToHex(digest.digest());
 
390
                            lastModified = new Date();
 
391
                            foundObject.setEtag(md5);
 
392
                            foundObject.setSize(size);
 
393
                            foundObject.setLastModified(lastModified);
 
394
                            foundObject.setStorageClass("STANDARD");
 
395
                            if(shouldEnforceUsageLimits && !request.isAdministrator()) {
 
396
                                Long bucketSize = bucket.getBucketSize();
 
397
                                long newSize = bucketSize + oldBucketSize + size;
 
398
                                if(newSize > WalrusProperties.MAX_BUCKET_SIZE) {
 
399
                                    db.rollback();
 
400
                                    throw new EntityTooLargeException(objectName);
 
401
                                }
 
402
                                bucket.setBucketSize(newSize);
 
403
                            }
 
404
                            db.commit();
 
405
                            //restart all interrupted puts
 
406
                            WalrusMonitor monitor = messenger.getMonitor(key);
 
407
                            synchronized (monitor) {
 
408
                                monitor.setLastModified(lastModified);
 
409
                                monitor.setMd5(md5);
 
410
                                monitor.notifyAll();
 
411
                            }
 
412
                            messenger.removeQueue(key, randomKey);
 
413
                            messenger.removeMonitor(key);
 
414
                            LOG.info("Transfer complete" + key + " " + randomKey);
 
415
                            break;
 
416
 
 
417
                        } else if(WalrusDataMessage.isInterrupted(dataMessage)) {
 
418
 
 
419
                            //there was a write after this one started
 
420
                            //abort writing but wait until the other (last) writer has completed
 
421
                            WalrusMonitor monitor = messenger.getMonitor(key);
 
422
                            synchronized (monitor) {
 
423
                                monitor.wait();
 
424
                                lastModified = monitor.getLastModified();
 
425
                                md5 = monitor.getMd5();
 
426
                            }
 
427
                            //ok we are done here
 
428
                            try {
 
429
                                storageManager.deleteObject(bucketName, tempObjectName);
 
430
                            } catch (IOException ex) {
 
431
                                ex.printStackTrace();
 
432
                            }
 
433
                            db.rollback();
 
434
                            LOG.info("Transfer interrupted" + key + " " + randomKey);
 
435
                            break;
 
436
                        } else {
 
437
                            assert(WalrusDataMessage.isData(dataMessage));
 
438
                            byte[] data = dataMessage.getPayload();
 
439
                            //start writing object (but do not committ yet)
 
440
                            try {
 
441
                                storageManager.putObject(bucketName, tempObjectName, data, true);
 
442
                            } catch (IOException ex) {
 
443
 
 
444
                            }
 
445
                            //calculate md5 on the fly
 
446
                            size += data.length;
 
447
                            digest.update(data);
 
448
                        }
 
449
                    }
 
450
                } catch (InterruptedException ex) {
 
451
                    ex.printStackTrace();
 
452
                    throw new EucalyptusCloudException();
 
453
                }
 
454
            } else {
 
455
                db.rollback();
 
456
                throw new AccessDeniedException(bucketName);
 
457
            }
 
458
        }   else {
 
459
            db.rollback();
 
460
            throw new NoSuchBucketException(bucketName);
 
461
        }
 
462
        reply.setEtag(md5);
 
463
        reply.setLastModified(DateUtils.format(lastModified.getTime(), DateUtils.ISO8601_DATETIME_PATTERN) + ".000Z");
 
464
        return reply;
 
465
    }
 
466
 
 
467
    public PutObjectInlineResponseType PutObjectInline (PutObjectInlineType request) throws EucalyptusCloudException {
 
468
        PutObjectInlineResponseType reply = (PutObjectInlineResponseType) request.getReply();
 
469
        String userId = request.getUserId();
 
470
 
 
471
        String bucketName = request.getBucket();
 
472
        String objectName = request.getKey();
 
473
 
 
474
        String md5 = "";
 
475
        Long oldBucketSize = 0L;
 
476
        Date lastModified = null;
 
477
 
 
478
        AccessControlListType accessControlList = request.getAccessControlList();
 
479
        if (accessControlList == null) {
 
480
            accessControlList = new AccessControlListType();
 
481
        }
 
482
 
 
483
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
484
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
485
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
486
 
 
487
        if(bucketList.size() > 0) {
 
488
            BucketInfo bucket = bucketList.get(0);
 
489
            if (bucket.canWrite(userId)) {
 
490
                ObjectInfo foundObject = null;
 
491
                List<ObjectInfo> objectInfos = bucket.getObjects();
 
492
                for (ObjectInfo objectInfo: objectInfos) {
 
493
                    if (objectInfo.getObjectName().equals(objectName)) {
 
494
                        //key (object) exists. check perms
 
495
                        if (!objectInfo.canWrite(userId)) {
 
496
                            db.rollback();
 
497
                            throw new AccessDeniedException(objectName);
 
498
                        }
 
499
                        foundObject = objectInfo;
 
500
                        oldBucketSize = -foundObject.getSize();
 
501
                        break;
 
502
                    }
 
503
                }
 
504
                //write object to bucket
 
505
                if (foundObject == null) {
 
506
                    //not found. create an object info
 
507
                    foundObject = new ObjectInfo(objectName);
 
508
                    List<GrantInfo> grantInfos = new ArrayList<GrantInfo>();
 
509
                    foundObject.addGrants(userId, grantInfos, accessControlList);
 
510
                    foundObject.setGrants(grantInfos);
 
511
                    objectInfos.add(foundObject);
 
512
                } else {
 
513
                    //object already exists. see if we can modify acl
 
514
                    if (foundObject.canWriteACP(userId)) {
 
515
                        List<GrantInfo> grantInfos = foundObject.getGrants();
 
516
                        foundObject.addGrants(userId, grantInfos, accessControlList);
 
517
                    }
 
518
                }
 
519
                foundObject.setObjectName(objectName);
 
520
                foundObject.setOwnerId(userId);
 
521
                try {
 
522
                    //writes are unconditional
 
523
                    byte[] base64Data = request.getBase64Data().getBytes();
 
524
                    storageManager.putObject(bucketName, objectName, base64Data, false);
 
525
                    md5 = Hashes.getHexString(Hashes.Digest.MD5.get().digest(base64Data));
 
526
                    foundObject.setEtag(md5);
 
527
                    Long size = Long.parseLong(request.getContentLength());
 
528
                    foundObject.setSize(size);
 
529
                    if(shouldEnforceUsageLimits && !request.isAdministrator()) {
 
530
                        Long bucketSize = bucket.getBucketSize();
 
531
                        long newSize = bucketSize + oldBucketSize + size;
 
532
                        if(newSize > WalrusProperties.MAX_BUCKET_SIZE) {
 
533
                            db.rollback();
 
534
                            throw new EntityTooLargeException(objectName);
 
535
                        }
 
536
                        bucket.setBucketSize(newSize);
 
537
                    }
 
538
                    //Add meta data if specified
 
539
                    foundObject.addMetaData(request.getMetaData());
 
540
 
 
541
                    //TODO: add support for other storage classes
 
542
                    foundObject.setStorageClass("STANDARD");
 
543
                    lastModified = new Date();
 
544
                    foundObject.setLastModified(lastModified);
 
545
                } catch (IOException ex) {
 
546
                    //TODO: set error code
 
547
                    LOG.warn(ex, ex);
 
548
                }
 
549
            } else {
 
550
                db.rollback();
 
551
                throw new AccessDeniedException(bucketName);
 
552
            }
 
553
 
 
554
        }   else {
 
555
            db.rollback();
 
556
            throw new NoSuchBucketException(bucketName);
 
557
        }
 
558
 
 
559
        db.commit();
 
560
 
 
561
        reply.setEtag(md5);
 
562
        reply.setLastModified(DateUtils.format(lastModified.getTime(), DateUtils.ISO8601_DATETIME_PATTERN) + ".000Z");
 
563
        return reply;
 
564
    }
 
565
 
 
566
    public DeleteObjectResponseType DeleteObject (DeleteObjectType request) throws EucalyptusCloudException {
 
567
        DeleteObjectResponseType reply = (DeleteObjectResponseType) request.getReply();
 
568
        String bucketName = request.getBucket();
 
569
        String objectName = request.getKey();
 
570
        String userId = request.getUserId();
 
571
 
 
572
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
573
        BucketInfo bucketInfos = new BucketInfo(bucketName);
 
574
        List<BucketInfo> bucketList = db.query(bucketInfos);
 
575
 
 
576
        if (bucketList.size() > 0) {
 
577
            BucketInfo bucketInfo = bucketList.get(0);
 
578
            ObjectInfo foundObject = null;
 
579
 
 
580
            for (ObjectInfo objectInfo: bucketInfo.getObjects()) {
 
581
                if (objectInfo.getObjectName().equals(objectName)) {
 
582
                    foundObject = objectInfo;
 
583
                }
 
584
            }
 
585
 
 
586
            if (foundObject != null) {
 
587
                if (foundObject.canWrite(userId)) {
 
588
                    bucketInfo.getObjects().remove(foundObject);
 
589
                    for (GrantInfo grantInfo: foundObject.getGrants()) {
 
590
                        db.getEntityManager().remove(grantInfo);
 
591
                    }
 
592
                    Long size = foundObject.getSize();
 
593
                    bucketInfo.setBucketSize(bucketInfo.getBucketSize() - size);
 
594
                    db.getEntityManager().remove(foundObject);
 
595
                    try {
 
596
                        storageManager.deleteObject(bucketName, objectName);
 
597
                    } catch (IOException ex) {
 
598
                        //TODO: set error code
 
599
                        LOG.warn(ex, ex);
 
600
                    }
 
601
                    reply.setCode("200");
 
602
                    reply.setDescription("OK");
 
603
                } else {
 
604
                    db.rollback();
 
605
                    throw new AccessDeniedException(objectName);
 
606
                }
 
607
            } else {
 
608
                db.rollback();
 
609
                throw new AccessDeniedException(objectName);
 
610
            }
 
611
        } else {
 
612
            db.rollback();
 
613
            throw new NoSuchBucketException(bucketName);
 
614
        }
 
615
        db.commit();
 
616
        return reply;
 
617
    }
 
618
 
 
619
    public ListBucketResponseType ListBucket(ListBucketType request) throws EucalyptusCloudException {
 
620
        ListBucketResponseType reply = (ListBucketResponseType) request.getReply();
 
621
        String bucketName = request.getBucket();
 
622
        String userId = request.getUserId();
 
623
        String prefix = request.getPrefix();
 
624
        String marker = request.getMarker();
 
625
 
 
626
        int maxKeys = -1;
 
627
        String maxKeysString = request.getMaxKeys();
 
628
        if(maxKeysString != null)
 
629
            maxKeys = Integer.parseInt(maxKeysString);
 
630
 
 
631
        String delimiter = request.getDelimiter();
 
632
 
 
633
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
634
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
635
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
636
 
 
637
        ArrayList<PrefixEntry> prefixes = new ArrayList<PrefixEntry>();
 
638
 
 
639
        if(bucketList.size() > 0) {
 
640
            BucketInfo bucket = bucketList.get(0);
 
641
            if(bucket.canRead(userId)) {
 
642
                reply.setName(bucketName);
 
643
                reply.setIsTruncated(false);
 
644
                if(maxKeys >= 0)
 
645
                    reply.setMaxKeys(maxKeys);
 
646
                if(prefix != null) {
 
647
                    reply.setPrefix(prefix);
 
648
                }
 
649
                if(marker != null)
 
650
                    reply.setMarker(marker);
 
651
                if(delimiter != null)
 
652
                    reply.setDelimiter(delimiter);
 
653
                List<ObjectInfo> objectInfos = bucket.getObjects();
 
654
                if(objectInfos.size() > 0) {
 
655
                    int howManyProcessed = 0;
 
656
                    ArrayList<ListEntry> contents = new ArrayList<ListEntry>();
 
657
                    for(ObjectInfo objectInfo: objectInfos) {
 
658
                        String objectName = objectInfo.getObjectName();
 
659
                        if(marker != null) {
 
660
                            if(objectName.compareTo(marker) < 0)
 
661
                                continue;
 
662
                        }
 
663
                        if(prefix != null) {
 
664
                            if(!objectName.startsWith(prefix)) {
 
665
                                continue;
 
666
                            } else {
 
667
                                if(delimiter != null) {
 
668
                                    String[] parts = objectName.substring(prefix.length()).split(delimiter);
 
669
                                    if(parts.length > 1) {
 
670
                                        String prefixString = parts[0] + delimiter;
 
671
                                        boolean foundPrefix = false;
 
672
                                        for(PrefixEntry prefixEntry : prefixes) {
 
673
                                            if(prefixEntry.getPrefix().equals(prefixString)) {
 
674
                                                foundPrefix = true;
 
675
                                                break;
 
676
                                            }
 
677
                                        }
 
678
                                        if(!foundPrefix) {
 
679
                                            prefixes.add(new PrefixEntry(prefixString));
 
680
                                            if(maxKeys >= 0) {
 
681
                                                if(howManyProcessed++ > maxKeys) {
 
682
                                                    reply.setIsTruncated(true);
 
683
                                                    break;
 
684
                                                }
 
685
                                            }
 
686
                                        }
 
687
                                        continue;
 
688
                                    }
 
689
                                }
 
690
                            }
 
691
                        }
 
692
                        if(maxKeys >= 0) {
 
693
                            if(howManyProcessed++ > maxKeys) {
 
694
                                reply.setIsTruncated(true);
 
695
                                break;
 
696
                            }
 
697
                        }
 
698
                        ListEntry listEntry = new ListEntry();
 
699
                        listEntry.setKey(objectName);
 
700
                        listEntry.setEtag(objectInfo.getEtag());
 
701
                        listEntry.setLastModified(DateUtils.format(objectInfo.getLastModified().getTime(), DateUtils.ISO8601_DATETIME_PATTERN) + ".000Z");
 
702
                        listEntry.setStorageClass(objectInfo.getStorageClass());
 
703
                        String displayName = objectInfo.getOwnerId();
 
704
 
 
705
                        EntityWrapper<UserInfo> db2 = new EntityWrapper<UserInfo>();
 
706
                        UserInfo userInfo = new UserInfo(displayName);
 
707
                        List<UserInfo> ownerInfos = db2.query(userInfo);
 
708
                        db2.commit();
 
709
                        if(ownerInfos.size() > 0) {
 
710
                            listEntry.setOwner(new CanonicalUserType(ownerInfos.get(0).getQueryId(), displayName));
 
711
                        }
 
712
                        ArrayList<MetaDataEntry> metaData = new ArrayList<MetaDataEntry>();
 
713
                        objectInfo.getMetaData(metaData);
 
714
                        reply.setMetaData(metaData);
 
715
                        listEntry.setSize(objectInfo.getSize());
 
716
                        listEntry.setStorageClass(objectInfo.getStorageClass());
 
717
                        contents.add(listEntry);
 
718
                    }
 
719
                    reply.setContents(contents);
 
720
                    if(prefix != null) {
 
721
                        reply.setCommonPrefixes(prefixes);
 
722
                    }
 
723
                }
 
724
            } else {
 
725
                db.rollback();
 
726
                throw new AccessDeniedException(bucketName);
 
727
            }
 
728
        } else {
 
729
            db.rollback();
 
730
            throw new NoSuchBucketException(bucketName);
 
731
        }
 
732
        db.commit();
 
733
        return reply;
 
734
    }
 
735
 
 
736
    public GetObjectAccessControlPolicyResponseType GetObjectAccessControlPolicy(GetObjectAccessControlPolicyType request) throws EucalyptusCloudException
 
737
    {
 
738
        GetObjectAccessControlPolicyResponseType reply = (GetObjectAccessControlPolicyResponseType) request.getReply();
 
739
 
 
740
        String bucketName = request.getBucket();
 
741
        String objectName = request.getKey();
 
742
        String userId = request.getUserId();
 
743
        String ownerId = null;
 
744
 
 
745
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
746
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
747
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
748
 
 
749
        AccessControlListType accessControlList = new AccessControlListType();
 
750
 
 
751
        if (bucketList.size() > 0) {
 
752
            //construct access control policy from grant infos
 
753
            BucketInfo bucket = bucketList.get(0);
 
754
            for(ObjectInfo objectInfo: bucket.getObjects()) {
 
755
                if (objectInfo.getObjectName().equals(objectName)) {
 
756
                    if(objectInfo.canReadACP(userId)) {
 
757
                        ownerId = objectInfo.getOwnerId();
 
758
                        ArrayList<Grant> grants = new ArrayList<Grant>();
 
759
                        List<GrantInfo> grantInfos = objectInfo.getGrants();
 
760
                        for (GrantInfo grantInfo: grantInfos) {
 
761
                            String uId = grantInfo.getUserId();
 
762
                            UserInfo userInfo = new UserInfo(uId);
 
763
                            EntityWrapper<UserInfo> db2 = new EntityWrapper<UserInfo>();
 
764
                            List<UserInfo> grantUserInfos = db2.query(userInfo);
 
765
                            db2.commit();
 
766
                            if(grantUserInfos.size() > 0) {
 
767
                                addPermission(grants, grantUserInfos.get(0), grantInfo);
 
768
                            }
 
769
                        }
 
770
                        accessControlList.setGrants(grants);
 
771
                    } else {
 
772
                        db.rollback();
 
773
                        throw new AccessDeniedException(objectName);
 
774
                    }
 
775
                }
 
776
            }
 
777
 
 
778
        }   else {
 
779
            db.rollback();
 
780
            throw new NoSuchBucketException(bucketName);
 
781
        }
 
782
        UserInfo userInfo = new UserInfo(ownerId);
 
783
        EntityWrapper<UserInfo> db2 = new EntityWrapper<UserInfo>();
 
784
        List<UserInfo> ownerUserInfos = db2.query(userInfo);
 
785
        db2.commit();
 
786
 
 
787
        AccessControlPolicyType accessControlPolicy = new AccessControlPolicyType();
 
788
        if(ownerUserInfos.size() > 0) {
 
789
            UserInfo ownerUserInfo = ownerUserInfos.get(0);
 
790
            accessControlPolicy.setOwner(new CanonicalUserType(ownerUserInfo.getQueryId(), ownerUserInfo.getUserName()));
 
791
            accessControlPolicy.setAccessControlList(accessControlList);
 
792
            reply.setAccessControlPolicy(accessControlPolicy);
 
793
        }
 
794
        db.commit();
 
795
        return reply;
 
796
    }
 
797
 
 
798
    public SetBucketAccessControlPolicyResponseType SetBucketAccessControlPolicy(SetBucketAccessControlPolicyType request) throws EucalyptusCloudException
 
799
    {
 
800
        SetBucketAccessControlPolicyResponseType reply = (SetBucketAccessControlPolicyResponseType) request.getReply();
 
801
        String userId = request.getUserId();
 
802
        AccessControlPolicyType accessControlPolicy = request.getAccessControlPolicy();
 
803
        if(accessControlPolicy == null) {
 
804
            throw new AccessDeniedException(userId);
 
805
        }
 
806
        String bucketName = request.getBucket();
 
807
 
 
808
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
809
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
810
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
811
 
 
812
        if (bucketList.size() > 0) {
 
813
            BucketInfo bucket = bucketList.get(0);
 
814
            if (bucket.canWriteACP(userId) && accessControlPolicy.getOwner().getDisplayName().equals(bucket.getOwnerId())) {
 
815
                List<GrantInfo> grantInfos = new ArrayList<GrantInfo>();
 
816
                AccessControlListType accessControlList = accessControlPolicy.getAccessControlList();
 
817
                bucket.resetGlobalGrants();
 
818
                bucket.addGrants(bucket.getOwnerId(), grantInfos, accessControlList);
 
819
                bucket.setGrants(grantInfos);
 
820
                reply.setCode("204");
 
821
                reply.setDescription("OK");
 
822
            } else {
 
823
                db.rollback();
 
824
                throw new AccessDeniedException(bucketName);
 
825
            }
 
826
        }   else {
 
827
            db.rollback();
 
828
            throw new NoSuchBucketException(bucketName);
 
829
        }
 
830
        db.commit();
 
831
        return reply;
 
832
    }
 
833
 
 
834
    public SetObjectAccessControlPolicyResponseType SetObjectAccessControlPolicy(SetObjectAccessControlPolicyType request) throws EucalyptusCloudException
 
835
    {
 
836
        SetObjectAccessControlPolicyResponseType reply = (SetObjectAccessControlPolicyResponseType) request.getReply();
 
837
        String userId = request.getUserId();
 
838
        AccessControlPolicyType accessControlPolicy = request.getAccessControlPolicy();
 
839
        String bucketName = request.getBucket();
 
840
        String objectName = request.getKey();
 
841
 
 
842
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
843
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
844
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
845
 
 
846
        if (bucketList.size() > 0) {
 
847
            BucketInfo bucket = bucketList.get(0);
 
848
            ObjectInfo foundObject = null;
 
849
            for(ObjectInfo objectInfo: bucket.getObjects()) {
 
850
                if(objectInfo.getObjectName().equals(objectName)) {
 
851
                    if (objectInfo.canWriteACP(userId) && accessControlPolicy.getOwner().getDisplayName().equals(objectInfo.getOwnerId())) {
 
852
                        foundObject = objectInfo;
 
853
                        break;
 
854
                    } else {
 
855
                        db.rollback();
 
856
                        throw new AccessDeniedException(objectName);
 
857
                    }
 
858
                }
 
859
            }
 
860
 
 
861
            if(foundObject != null) {
 
862
                List<GrantInfo> grantInfos = new ArrayList<GrantInfo>();
 
863
                AccessControlListType accessControlList = accessControlPolicy.getAccessControlList();
 
864
                foundObject.resetGlobalGrants();
 
865
                foundObject.addGrants(foundObject.getOwnerId(), grantInfos, accessControlList);
 
866
                foundObject.setGrants(grantInfos);
 
867
 
 
868
                reply.setCode("204");
 
869
                reply.setDescription("OK");
 
870
            } else {
 
871
                db.rollback();
 
872
                throw new NoSuchEntityException(objectName);
 
873
            }
 
874
        }   else {
 
875
            db.rollback();
 
876
            throw new NoSuchBucketException(bucketName);
 
877
        }
 
878
        db.commit();
 
879
        return reply;
 
880
    }
 
881
 
 
882
    public GetObjectResponseType GetObject(GetObjectType request) throws EucalyptusCloudException {
 
883
        GetObjectResponseType reply = (GetObjectResponseType) request.getReply();
 
884
        String bucketName = request.getBucket();
 
885
        String objectName = request.getKey();
 
886
        String userId = request.getUserId();
 
887
 
 
888
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
889
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
890
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
891
 
 
892
        if (bucketList.size() > 0) {
 
893
            BucketInfo bucket = bucketList.get(0);
 
894
 
 
895
            for(ObjectInfo objectInfo: bucket.getObjects()) {
 
896
                if(objectInfo.getObjectName().equals(objectName)) {
 
897
                    if(objectInfo.canRead(userId)) {
 
898
                        if(request.getGetMetaData()) {
 
899
                            ArrayList<MetaDataEntry> metaData = new ArrayList<MetaDataEntry>();
 
900
                            objectInfo.getMetaData(metaData);
 
901
                            reply.setMetaData(metaData);
 
902
                        }
 
903
                        if(request.getGetData()) {
 
904
                            if(request.getInlineData()) {
 
905
                                try {
 
906
                                    byte[] bytes = new byte[WalrusQueryDispatcher.DATA_MESSAGE_SIZE];
 
907
                                    int bytesRead = 0;
 
908
                                    String base64Data = "";
 
909
                                    while((bytesRead = storageManager.readObject(bucketName, objectName, bytes, bytesRead)) > 0) {
 
910
                                        base64Data += new String(bytes, 0, bytesRead);
 
911
                                    }
 
912
                                    reply.setBase64Data(base64Data);
 
913
                                } catch (IOException ex) {
 
914
                                    db.rollback();
 
915
                                    //set error code
 
916
                                    return reply;
 
917
                                }
 
918
                            } else {
 
919
                                //support for large objects
 
920
                                String key = bucketName + "." + objectName;
 
921
                                String randomKey = key + "." + Hashes.getRandom(10);
 
922
                                request.setRandomKey(randomKey);
 
923
                                LinkedBlockingQueue<WalrusDataMessage> getQueue = WalrusQueryDispatcher.getReadMessenger().getQueue(key, randomKey);
 
924
 
 
925
                                Reader reader = new Reader(bucketName, objectName, objectInfo.getSize(), getQueue);
 
926
                                reader.start();
 
927
                            }
 
928
                        }
 
929
                        reply.setEtag(objectInfo.getEtag());
 
930
                        reply.setLastModified(DateUtils.format(objectInfo.getLastModified().getTime(), DateUtils.ISO8601_DATETIME_PATTERN) + ".000Z");
 
931
                        reply.setSize(objectInfo.getSize());
 
932
                        Status status = new Status();
 
933
                        status.setCode(200);
 
934
                        status.setDescription("OK");
 
935
                        reply.setStatus(status);
 
936
                    } else {
 
937
                        db.rollback();
 
938
                        throw new AccessDeniedException(objectName);
 
939
                    }
 
940
                }
 
941
            }
 
942
        } else {
 
943
            db.rollback();
 
944
            throw new NoSuchBucketException(bucketName);
 
945
        }
 
946
        db.commit();
 
947
        return reply;
 
948
    }
 
949
 
 
950
    public GetObjectExtendedResponseType GetObjectExtended(GetObjectExtendedType request) throws EucalyptusCloudException {
 
951
        GetObjectExtendedResponseType reply = (GetObjectExtendedResponseType) request.getReply();
 
952
        long byteRangeStart = request.getByteRangeStart();
 
953
        long byteRangeEnd = request.getByteRangeEnd();
 
954
        Date ifModifiedSince = request.getIfModifiedSince();
 
955
        Date ifUnmodifiedSince = request.getIfUnmodifiedSince();
 
956
        String ifMatch = request.getIfMatch();
 
957
        String ifNoneMatch = request.getIfNoneMatch();
 
958
        boolean returnCompleteObjectOnFailure = request.getReturnCompleteObjectOnConditionFailure();
 
959
 
 
960
        String bucketName = request.getBucket();
 
961
        String objectName = request.getKey();
 
962
        String userId = request.getUserId();
 
963
        Status status = new Status();
 
964
 
 
965
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
966
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
967
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
968
 
 
969
 
 
970
        if (bucketList.size() > 0) {
 
971
            BucketInfo bucket = bucketList.get(0);
 
972
 
 
973
            for(ObjectInfo objectInfo: bucket.getObjects()) {
 
974
                if(objectInfo.getObjectName().equals(objectName)) {
 
975
                    if(objectInfo.canRead(userId)) {
 
976
                        String etag = objectInfo.getEtag();
 
977
                        if(ifMatch != null) {
 
978
                            if(!ifMatch.equals(etag) && !returnCompleteObjectOnFailure) {
 
979
                                db.rollback();
 
980
                                throw new PreconditionFailedException(etag);
 
981
                            }
 
982
 
 
983
                        }
 
984
                        if(ifNoneMatch != null) {
 
985
                            if(ifNoneMatch.equals(etag) && !returnCompleteObjectOnFailure) {
 
986
                                db.rollback();
 
987
                                throw new NotModifiedException(etag);
 
988
                            }
 
989
                        }
 
990
                        Date lastModified = objectInfo.getLastModified();
 
991
                        if(ifModifiedSince != null) {
 
992
                            if((ifModifiedSince.getTime() >= lastModified.getTime()) && !returnCompleteObjectOnFailure) {
 
993
                                db.rollback();
 
994
                                throw new NotModifiedException(lastModified.toString());
 
995
                            }
 
996
                        }
 
997
                        if(ifUnmodifiedSince != null) {
 
998
                            if((ifUnmodifiedSince.getTime() <= lastModified.getTime()) && !returnCompleteObjectOnFailure) {
 
999
                                db.rollback();
 
1000
                                throw new PreconditionFailedException(lastModified.toString());
 
1001
                            }
 
1002
                        }
 
1003
                        if(request.getGetMetaData()) {
 
1004
                            ArrayList<MetaDataEntry> metaData = new ArrayList<MetaDataEntry>();
 
1005
                            objectInfo.getMetaData(metaData);
 
1006
                            reply.setMetaData(metaData);
 
1007
                        }
 
1008
                        if(request.getGetData()) {
 
1009
                            String key = bucketName + "." + objectName;
 
1010
                            String randomKey = key + "." + Hashes.getRandom(10);
 
1011
                            request.setRandomKey(randomKey);
 
1012
                            LinkedBlockingQueue<WalrusDataMessage> getQueue = WalrusQueryDispatcher.getReadMessenger().getQueue(key, randomKey);
 
1013
 
 
1014
                            Reader reader = new Reader(bucketName, objectName, objectInfo.getSize(), getQueue, byteRangeStart, byteRangeEnd);
 
1015
                            reader.start();
 
1016
                        }
 
1017
                        reply.setEtag(objectInfo.getEtag());
 
1018
                        reply.setLastModified(DateUtils.format(objectInfo.getLastModified().getTime(), DateUtils.ISO8601_DATETIME_PATTERN) + ".000Z");
 
1019
                        reply.setSize(objectInfo.getSize());
 
1020
                        status.setCode(200);
 
1021
                        status.setDescription("OK");
 
1022
                        reply.setStatus(status);
 
1023
                    } else {
 
1024
                        db.rollback();
 
1025
                        throw new AccessDeniedException(objectName);
 
1026
                    }
 
1027
                }
 
1028
            }
 
1029
        }
 
1030
        db.commit();
 
1031
        return reply;
 
1032
    }
 
1033
 
 
1034
    public GetBucketLocationResponseType GetBucketLocation(GetBucketLocationType request) throws EucalyptusCloudException {
 
1035
        GetBucketLocationResponseType reply = (GetBucketLocationResponseType) request.getReply();
 
1036
        String bucketName = request.getBucket();
 
1037
        String userId = request.getUserId();
 
1038
 
 
1039
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
1040
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
1041
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
1042
 
 
1043
        if(bucketList.size() > 0) {
 
1044
            BucketInfo bucket = bucketList.get(0);
 
1045
            if(bucket.canRead(userId)) {
 
1046
                String location = bucket.getLocation();
 
1047
                if(location == null) {
 
1048
                    location = "NotSupported";
 
1049
                }
 
1050
                reply.setLocationConstraint(location);
 
1051
            } else {
 
1052
                db.rollback();
 
1053
                throw new AccessDeniedException(userId);
 
1054
            }
 
1055
        } else {
 
1056
            db.rollback();
 
1057
            throw new NoSuchBucketException(bucketName);
 
1058
        }
 
1059
        db.commit();
 
1060
        return reply;
 
1061
    }
 
1062
 
 
1063
    public CopyObjectResponseType CopyObject(CopyObjectType request) throws EucalyptusCloudException {
 
1064
        CopyObjectResponseType reply = (CopyObjectResponseType) request.getReply();
 
1065
 
 
1066
        throw new NotImplementedException("CopyObject");
 
1067
    }
 
1068
 
 
1069
    public GetBucketLoggingStatusResponseType GetBucketLoggingStatus(GetBucketLoggingStatusType request) throws EucalyptusCloudException {
 
1070
        GetBucketLoggingStatusResponseType reply = (GetBucketLoggingStatusResponseType) request.getReply();
 
1071
 
 
1072
        throw new NotImplementedException("GetBucketLoggingStatus");
 
1073
    }
 
1074
 
 
1075
    public SetBucketLoggingStatusResponseType SetBucketLoggingStatus(SetBucketLoggingStatusType request) throws EucalyptusCloudException {
 
1076
        SetBucketLoggingStatusResponseType reply = (SetBucketLoggingStatusResponseType) request.getReply();
 
1077
 
 
1078
        throw new NotImplementedException("SetBucketLoggingStatus");
 
1079
    }
 
1080
 
 
1081
    private boolean canVerifySignature(Signature sigVerifier, X509Certificate cert, String signature, String verificationString) throws Exception {
 
1082
        PublicKey publicKey = cert.getPublicKey();
 
1083
        sigVerifier.initVerify(publicKey);
 
1084
        sigVerifier.update((verificationString).getBytes());
 
1085
        return sigVerifier.verify(hexToBytes(signature));
 
1086
    }
 
1087
 
 
1088
    private String decryptImage(String bucketName, String objectName, String userId, boolean isAdministrator) throws EucalyptusCloudException {
 
1089
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
1090
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
1091
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
1092
 
 
1093
 
 
1094
        if (bucketList.size() > 0) {
 
1095
            BucketInfo bucket = bucketList.get(0);
 
1096
 
 
1097
            for(ObjectInfo objectInfo: bucket.getObjects()) {
 
1098
                if(objectInfo.getObjectName().equals(objectName)) {
 
1099
                    if(objectInfo.canRead(userId)) {
 
1100
                        File file = new File(storageManager.getObjectPath(bucketName, objectName));
 
1101
                        XMLParser parser = new XMLParser(file);
 
1102
//Read manifest
 
1103
                        String imageKey = parser.getValue("//image/name");
 
1104
                        String encryptedKey = parser.getValue("//ec2_encrypted_key");
 
1105
                        String encryptedIV = parser.getValue("//ec2_encrypted_iv");
 
1106
                        String signature = parser.getValue("//signature");
 
1107
 
 
1108
                        AbstractKeyStore userKeyStore = UserKeyStore.getInstance();
 
1109
 
 
1110
                        String image = parser.getXML("image");
 
1111
                        String machineConfiguration = parser.getXML("machine_configuration");
 
1112
 
 
1113
                        String verificationString = machineConfiguration + image;
 
1114
 
 
1115
                        Signature sigVerifier;
 
1116
                        try {
 
1117
                            sigVerifier = Signature.getInstance("SHA1withRSA");
 
1118
                        } catch (NoSuchAlgorithmException ex) {
 
1119
                            LOG.error(ex, ex);
 
1120
                            throw new DecryptionFailedException("SHA1withRSA not found");
 
1121
                        }
 
1122
 
 
1123
                        EntityWrapper<UserInfo> db2 = new EntityWrapper<UserInfo>();
 
1124
                        UserInfo userInfo = new UserInfo(userId);
 
1125
                        List<UserInfo> foundUserInfos = db2.query(userInfo);
 
1126
                        if(foundUserInfos.size() == 0) {
 
1127
                            db2.rollback();
 
1128
                            db.rollback();
 
1129
                            throw new AccessDeniedException(userId);
 
1130
                        }
 
1131
 
 
1132
                        if(isAdministrator) {
 
1133
                            try {
 
1134
                                boolean verified = false;
 
1135
                                List<String> aliases = userKeyStore.getAliases();
 
1136
                                for(String alias : aliases) {
 
1137
                                    X509Certificate cert = userKeyStore.getCertificate(alias);
 
1138
                                    verified = canVerifySignature(sigVerifier, cert, signature, verificationString);
 
1139
                                    if(verified)
 
1140
                                        break;
 
1141
                                }
 
1142
                                if(!verified) {
 
1143
                                    throw new NotAuthorizedException("Invalid signature");
 
1144
                                }
 
1145
                            } catch (Exception ex) {
 
1146
                                db2.rollback();
 
1147
                                db.rollback();
 
1148
                                LOG.error(ex, ex);
 
1149
                                throw new DecryptionFailedException("signature verification");
 
1150
                            }
 
1151
                        } else {
 
1152
                            List<CertificateInfo> certInfos = foundUserInfos.get(0).getCertificates();
 
1153
                            boolean signatureVerified = false;
 
1154
                            for(CertificateInfo certInfo: certInfos) {
 
1155
                                String alias = certInfo.getCertAlias();
 
1156
                                try {
 
1157
                                    X509Certificate cert = userKeyStore.getCertificate(alias);
 
1158
                                    signatureVerified = canVerifySignature(sigVerifier, cert, signature, verificationString);
 
1159
                                    if (signatureVerified)
 
1160
                                        break;
 
1161
                                } catch(Exception ex) {
 
1162
                                    db2.rollback();
 
1163
                                    db.rollback();
 
1164
                                    LOG.error(ex, ex);
 
1165
                                    throw new DecryptionFailedException("signature verification");
 
1166
                                }
 
1167
                            }
 
1168
                            if(!signatureVerified) {
 
1169
                                throw new NotAuthorizedException("Invalid signature");
 
1170
                            }
 
1171
                        }
 
1172
                        List<String> parts = parser.getValues("//image/parts/part/filename");
 
1173
                        ArrayList<String> qualifiedPaths = new ArrayList<String>();
 
1174
 
 
1175
                        for (String part: parts) {
 
1176
                            qualifiedPaths.add(storageManager.getObjectPath(bucketName, part));
 
1177
                        }
 
1178
                        //Assemble parts
 
1179
                        String encryptedImageKey = imageKey + "-" + Hashes.getRandom(5) + ".crypt.gz";
 
1180
                        String encryptedImageName = storageManager.getObjectPath(bucketName, encryptedImageKey);
 
1181
                        String decryptedImageKey = encryptedImageKey.replaceAll("crypt.gz", "tgz");
 
1182
                        String decryptedImageName = storageManager.getObjectPath(bucketName, decryptedImageKey);
 
1183
                        assembleParts(encryptedImageName, qualifiedPaths);
 
1184
//Decrypt key and IV
 
1185
 
 
1186
                        byte[] key;
 
1187
                        byte[] iv;
 
1188
                        try {
 
1189
                            PrivateKey pk = (PrivateKey) userKeyStore.getKey(EucalyptusProperties.NAME, EucalyptusProperties.NAME);
 
1190
                            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
 
1191
                            cipher.init(Cipher.DECRYPT_MODE, pk);
 
1192
                            key = hexToBytes(new String(cipher.doFinal(hexToBytes(encryptedKey))));
 
1193
                            iv = hexToBytes(new String(cipher.doFinal(hexToBytes(encryptedIV))));
 
1194
                        } catch(Exception ex) {
 
1195
                            db2.rollback();
 
1196
                            db.rollback();
 
1197
                            LOG.error(ex, ex);
 
1198
                            throw new DecryptionFailedException("AES params");
 
1199
                        }
 
1200
 
 
1201
                        //Unencrypt image
 
1202
                        try {
 
1203
                            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
 
1204
                            IvParameterSpec salt = new IvParameterSpec(iv);
 
1205
                            SecretKey keySpec = new SecretKeySpec(key, "AES");
 
1206
                            cipher.init(Cipher.DECRYPT_MODE, keySpec, salt);
 
1207
                            decryptImage(encryptedImageName, decryptedImageName, cipher);
 
1208
                        } catch (Exception ex) {
 
1209
                            db2.rollback();
 
1210
                            db.rollback();
 
1211
                            LOG.error(ex, ex);
 
1212
                            throw new DecryptionFailedException("decryption failed");
 
1213
                        }
 
1214
                        try {
 
1215
                            storageManager.deleteAbsoluteObject(encryptedImageName);
 
1216
                        } catch (Exception ex) {
 
1217
                            LOG.warn(ex, ex);
 
1218
                            throw new EucalyptusCloudException();
 
1219
                        }
 
1220
                        db2.commit();
 
1221
                        db.commit();
 
1222
                        return decryptedImageKey;
 
1223
                    }
 
1224
                }
 
1225
            }
 
1226
        }
 
1227
        return null;
 
1228
    }
 
1229
 
 
1230
    private void checkManifest(String bucketName, String objectName, String userId) throws EucalyptusCloudException {
 
1231
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
1232
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
1233
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
1234
 
 
1235
 
 
1236
        if (bucketList.size() > 0) {
 
1237
            BucketInfo bucket = bucketList.get(0);
 
1238
 
 
1239
            for(ObjectInfo objectInfo: bucket.getObjects()) {
 
1240
                if(objectInfo.getObjectName().equals(objectName)) {
 
1241
                    if(objectInfo.canRead(userId)) {
 
1242
                        File file = new File(storageManager.getObjectPath(bucketName, objectName));
 
1243
                        XMLParser parser = new XMLParser(file);
 
1244
//Read manifest
 
1245
                        String imageKey = parser.getValue("//image/name");
 
1246
                        String encryptedKey = parser.getValue("//ec2_encrypted_key");
 
1247
                        String encryptedIV = parser.getValue("//ec2_encrypted_iv");
 
1248
                        String signature = parser.getValue("//signature");
 
1249
 
 
1250
                        AbstractKeyStore userKeyStore = UserKeyStore.getInstance();
 
1251
 
 
1252
                        String image = parser.getXML("image");
 
1253
                        String machineConfiguration = parser.getXML("machine_configuration");
 
1254
 
 
1255
                        EntityWrapper<UserInfo> db2 = new EntityWrapper<UserInfo>();
 
1256
                        UserInfo userInfo = new UserInfo(userId);
 
1257
                        List<UserInfo> foundUserInfos = db2.query(userInfo);
 
1258
                        if(foundUserInfos.size() == 0) {
 
1259
                            db2.rollback();
 
1260
                            db.rollback();
 
1261
                            throw new AccessDeniedException(userId);
 
1262
                        }
 
1263
 
 
1264
                        List<CertificateInfo> certInfos = foundUserInfos.get(0).getCertificates();
 
1265
                        boolean signatureVerified = false;
 
1266
 
 
1267
                        Signature sigVerifier;
 
1268
                        try {
 
1269
                            sigVerifier = Signature.getInstance("SHA1withRSA");
 
1270
                        } catch (NoSuchAlgorithmException ex) {
 
1271
                            LOG.error(ex, ex);
 
1272
                            throw new DecryptionFailedException("SHA1withRSA not found");
 
1273
                        }
 
1274
 
 
1275
                        for(CertificateInfo certInfo: certInfos) {
 
1276
                            String alias = certInfo.getCertAlias();
 
1277
                            try {
 
1278
                                X509Certificate cert = (X509Certificate) userKeyStore.getCertificate(alias);
 
1279
                                PublicKey publicKey = cert.getPublicKey();
 
1280
                                sigVerifier.initVerify(publicKey);
 
1281
                                sigVerifier.update((machineConfiguration + image).getBytes());
 
1282
                                signatureVerified = sigVerifier.verify(hexToBytes(signature));
 
1283
                                if (signatureVerified) {
 
1284
                                    break;
 
1285
                                }
 
1286
                            } catch(Exception ex) {
 
1287
                                db2.rollback();
 
1288
                                db.rollback();
 
1289
                                LOG.error(ex, ex);
 
1290
                                throw new DecryptionFailedException("signature verification");
 
1291
                            }
 
1292
                        }
 
1293
 
 
1294
                        if(!signatureVerified) {
 
1295
                            throw new NotAuthorizedException("Invalid signature");
 
1296
                        }
 
1297
                        //Decrypt key and IV
 
1298
 
 
1299
                        byte[] key;
 
1300
                        byte[] iv;
 
1301
                        try {
 
1302
                            PrivateKey pk = (PrivateKey) userKeyStore.getKey(EucalyptusProperties.NAME, EucalyptusProperties.NAME);
 
1303
                            Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
 
1304
                            cipher.init(Cipher.DECRYPT_MODE, pk);
 
1305
                            key = hexToBytes(new String(cipher.doFinal(hexToBytes(encryptedKey))));
 
1306
                            iv = hexToBytes(new String(cipher.doFinal(hexToBytes(encryptedIV))));
 
1307
                        } catch(Exception ex) {
 
1308
                            db2.rollback();
 
1309
                            db.rollback();
 
1310
                            LOG.error(ex, ex);
 
1311
                            throw new DecryptionFailedException("AES params");
 
1312
                        }
 
1313
                        db2.commit();
 
1314
                        db.commit();
 
1315
                    }
 
1316
                }
 
1317
            }
 
1318
        }
 
1319
    }
 
1320
 
 
1321
    public GetDecryptedImageResponseType GetDecryptedImage(GetDecryptedImageType request) throws EucalyptusCloudException {
 
1322
        GetDecryptedImageResponseType reply = (GetDecryptedImageResponseType) request.getReply();
 
1323
        String bucketName = request.getBucket();
 
1324
        String objectName = request.getKey();
 
1325
        String userId = request.getUserId();
 
1326
 
 
1327
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
1328
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
1329
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
1330
 
 
1331
        if (bucketList.size() > 0) {
 
1332
            BucketInfo bucket = bucketList.get(0);
 
1333
 
 
1334
            for(ObjectInfo objectInfo: bucket.getObjects()) {
 
1335
                if(objectInfo.getObjectName().equals(objectName)) {
 
1336
                    if(objectInfo.canRead(userId) || request.isAdministrator() ) {
 
1337
                        WalrusSemaphore semaphore = imageMessenger.getSemaphore(bucketName + "/" + objectName);
 
1338
                        try {
 
1339
                            semaphore.acquire();
 
1340
                        } catch(InterruptedException ex) {
 
1341
                            throw new EucalyptusCloudException("semaphore could not be acquired");
 
1342
                        }
 
1343
                        EntityWrapper<ImageCacheInfo> db2 = new EntityWrapper<ImageCacheInfo>();
 
1344
                        ImageCacheInfo searchImageCacheInfo = new ImageCacheInfo(bucketName, objectName);
 
1345
                        List<ImageCacheInfo> foundImageCacheInfos = db2.query(searchImageCacheInfo);
 
1346
 
 
1347
                        if(foundImageCacheInfos.size() == 0) {
 
1348
                            db2.commit();
 
1349
//issue a cache request
 
1350
                            cacheImage(bucketName, objectName, userId, request.isAdministrator());
 
1351
//query db again
 
1352
                            db2 = new EntityWrapper<ImageCacheInfo>();
 
1353
                            foundImageCacheInfos = db2.query(searchImageCacheInfo);
 
1354
                        }
 
1355
                        ImageCacheInfo foundImageCacheInfo = foundImageCacheInfos.get(0);
 
1356
                        if(!foundImageCacheInfo.getInCache()) {
 
1357
                            WalrusMonitor monitor = imageMessenger.getMonitor(bucketName + "/" + objectName);
 
1358
                            synchronized (monitor) {
 
1359
                                try {
 
1360
                                    monitor.wait();
 
1361
                                } catch(Exception ex) {
 
1362
                                    LOG.warn(ex, ex);
 
1363
                                    db2.rollback();
 
1364
                                    db.rollback();
 
1365
                                    throw new EucalyptusCloudException("monitor failure");
 
1366
                                }
 
1367
                            }
 
1368
                            //caching may have modified the db. repeat the query
 
1369
                            db2.commit();
 
1370
                            db2 = new EntityWrapper<ImageCacheInfo>();
 
1371
                            foundImageCacheInfos = db2.query(searchImageCacheInfo);
 
1372
                            if(foundImageCacheInfos.size() > 0) {
 
1373
                                foundImageCacheInfo = foundImageCacheInfos.get(0);
 
1374
                                foundImageCacheInfo.setUseCount(foundImageCacheInfo.getUseCount() + 1);
 
1375
                                assert(foundImageCacheInfo.getInCache());
 
1376
                            } else {
 
1377
                                db.rollback();
 
1378
                                db2.rollback();
 
1379
                                throw new NoSuchEntityException(objectName);
 
1380
                            }
 
1381
                        }
 
1382
 
 
1383
                        Long unencryptedSize = foundImageCacheInfo.getSize();
 
1384
                        String imageKey = foundImageCacheInfo.getImageName();
 
1385
                        String queueKey = bucketName + "." + objectName;
 
1386
                        String randomKey = queueKey + "." + Hashes.getRandom(10);
 
1387
                        request.setRandomKey(randomKey);
 
1388
 
 
1389
                        LinkedBlockingQueue<WalrusDataMessage> getQueue = WalrusQueryDispatcher.getReadMessenger().getQueue(queueKey, randomKey);
 
1390
                        reply.setSize(unencryptedSize);
 
1391
                        reply.setLastModified(DateUtils.format(objectInfo.getLastModified().getTime(), DateUtils.ISO8601_DATETIME_PATTERN) + ".000Z");
 
1392
                        reply.setEtag("");
 
1393
                        Reader reader = new Reader(bucketName, imageKey, unencryptedSize, getQueue, false, semaphore);
 
1394
                        reader.start();
 
1395
                        db.commit();
 
1396
                        db2.commit();
 
1397
                        return reply;
 
1398
                    } else {
 
1399
                        db.rollback();
 
1400
                        throw new AccessDeniedException(objectName);
 
1401
                    }
 
1402
                }
 
1403
            }
 
1404
            db.rollback();
 
1405
            throw new NoSuchEntityException(objectName);
 
1406
        } else {
 
1407
            db.rollback();
 
1408
            throw new NoSuchBucketException(bucketName);
 
1409
        }
 
1410
    }
 
1411
 
 
1412
    public CheckImageResponseType CheckImage(CheckImageType request) throws EucalyptusCloudException {
 
1413
        CheckImageResponseType reply = (CheckImageResponseType) request.getReply();
 
1414
        reply.setSuccess(false);
 
1415
        String bucketName = request.getBucket();
 
1416
        String objectName = request.getKey();
 
1417
        String userId = request.getUserId();
 
1418
 
 
1419
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
1420
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
1421
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
1422
 
 
1423
 
 
1424
        if (bucketList.size() > 0) {
 
1425
            BucketInfo bucket = bucketList.get(0);
 
1426
 
 
1427
            for(ObjectInfo objectInfo: bucket.getObjects()) {
 
1428
                if(objectInfo.getObjectName().equals(objectName)) {
 
1429
                    if(objectInfo.canRead(userId)) {
 
1430
                        checkManifest(bucketName, objectName, userId);
 
1431
                        reply.setSuccess(true);
 
1432
                        db.commit();
 
1433
                        return reply;
 
1434
                    } else {
 
1435
                        db.rollback();
 
1436
                        throw new AccessDeniedException(objectName);
 
1437
                    }
 
1438
                }
 
1439
            }
 
1440
            db.rollback();
 
1441
            throw new NoSuchEntityException(objectName);
 
1442
        } else {
 
1443
            db.rollback();
 
1444
            throw new NoSuchBucketException(bucketName);
 
1445
        }
 
1446
    }
 
1447
 
 
1448
    private void cacheImage(String bucketName, String manifestKey, String userId, boolean isAdministrator) throws EucalyptusCloudException {
 
1449
        EntityWrapper<ImageCacheInfo> db = new EntityWrapper<ImageCacheInfo>();
 
1450
        ImageCacheInfo searchImageCacheInfo = new ImageCacheInfo(bucketName, manifestKey);
 
1451
        List<ImageCacheInfo> imageCacheInfos = db.query(searchImageCacheInfo);
 
1452
 
 
1453
        if(imageCacheInfos.size() == 0) {
 
1454
            String decryptedImageKey = decryptImage(bucketName, manifestKey, userId, isAdministrator);
 
1455
//decryption worked. Add it.
 
1456
            ImageCacheInfo foundImageCacheInfo = new ImageCacheInfo(bucketName, manifestKey);
 
1457
            foundImageCacheInfo.setImageName(decryptedImageKey);
 
1458
            foundImageCacheInfo.setInCache(false);
 
1459
            foundImageCacheInfo.setCaching(true);
 
1460
            foundImageCacheInfo.setUseCount(0);
 
1461
            foundImageCacheInfo.setSize(0L);
 
1462
            db.add(foundImageCacheInfo);
 
1463
            db.commit();
 
1464
//decrypt, unzip, untar image in the background
 
1465
            ImageCacher imageCacher = new ImageCacher(bucketName, manifestKey, decryptedImageKey);
 
1466
            imageCacher.run();
 
1467
        } else {
 
1468
            db.rollback();
 
1469
            throw new ImageAlreadyExistsException(manifestKey);
 
1470
        }
 
1471
    }
 
1472
 
 
1473
    public CacheImageResponseType CacheImage(CacheImageType request) throws EucalyptusCloudException {
 
1474
        CacheImageResponseType reply = (CacheImageResponseType) request.getReply();
 
1475
        reply.setSuccess(false);
 
1476
        String bucketName = request.getBucket();
 
1477
        String manifestKey = request.getKey();
 
1478
        String userId = request.getUserId();
 
1479
 
 
1480
        EntityWrapper<BucketInfo> db = new EntityWrapper<BucketInfo>();
 
1481
        BucketInfo bucketInfo = new BucketInfo(bucketName);
 
1482
        List<BucketInfo> bucketList = db.query(bucketInfo);
 
1483
 
 
1484
 
 
1485
        if (bucketList.size() > 0) {
 
1486
            BucketInfo bucket = bucketList.get(0);
 
1487
 
 
1488
            for(ObjectInfo objectInfo: bucket.getObjects()) {
 
1489
                if(objectInfo.getObjectName().equals(manifestKey)) {
 
1490
                    if(objectInfo.canRead(userId)) {
 
1491
                        EntityWrapper<ImageCacheInfo> db2 = new EntityWrapper<ImageCacheInfo>();
 
1492
                        ImageCacheInfo searchImageCacheInfo = new ImageCacheInfo(bucketName, manifestKey);
 
1493
                        List<ImageCacheInfo> foundImageCacheInfos = db2.query(searchImageCacheInfo);
 
1494
                        db2.commit();
 
1495
                        if(foundImageCacheInfos.size() == 0) {
 
1496
                            cacheImage(bucketName, manifestKey, userId, request.isAdministrator());
 
1497
                            reply.setSuccess(true);
 
1498
                        }
 
1499
                        return reply;
 
1500
                    } else {
 
1501
                        db.rollback();
 
1502
                        throw new AccessDeniedException(manifestKey);
 
1503
                    }
 
1504
 
 
1505
                }
 
1506
            }
 
1507
        }
 
1508
        throw new NoSuchEntityException(manifestKey);
 
1509
    }
 
1510
 
 
1511
    public FlushCachedImageResponseType FlushCachedImage(FlushCachedImageType request) throws EucalyptusCloudException {
 
1512
        FlushCachedImageResponseType reply = (FlushCachedImageResponseType) request.getReply();
 
1513
 
 
1514
        String bucketName = request.getBucket();
 
1515
        String manifestKey = request.getKey();
 
1516
 
 
1517
        EntityWrapper<ImageCacheInfo> db = new EntityWrapper<ImageCacheInfo>();
 
1518
        ImageCacheInfo searchImageCacheInfo = new ImageCacheInfo(bucketName, manifestKey);
 
1519
        List<ImageCacheInfo> foundImageCacheInfos = db.query(searchImageCacheInfo);
 
1520
 
 
1521
        if(foundImageCacheInfos.size() > 0) {
 
1522
            ImageCacheInfo foundImageCacheInfo = foundImageCacheInfos.get(0);
 
1523
            if(foundImageCacheInfo.getInCache() && !foundImageCacheInfo.getCaching()) {
 
1524
//check that there are no operations in progress and then flush cache and delete image file
 
1525
                db.commit();
 
1526
                ImageCacheFlusher imageCacheFlusher = new ImageCacheFlusher(bucketName, manifestKey);
 
1527
                imageCacheFlusher.start();
 
1528
            } else {
 
1529
                db.rollback();
 
1530
                throw new EucalyptusCloudException("not in cache");
 
1531
            }
 
1532
        } else {
 
1533
            db.rollback();
 
1534
            throw new NoSuchEntityException(bucketName + manifestKey);
 
1535
        }
 
1536
        return reply;
 
1537
    }
 
1538
 
 
1539
    private void flushCachedImage (String bucketName, String objectName) {
 
1540
        WalrusSemaphore semaphore = imageMessenger.getSemaphore(bucketName + "/" + objectName);
 
1541
        while(semaphore.inUse()) {
 
1542
            try {
 
1543
                synchronized (semaphore) {
 
1544
                    semaphore.wait();
 
1545
                }
 
1546
            } catch(InterruptedException ex) {
 
1547
                LOG.warn(ex, ex);
 
1548
            }
 
1549
        }
 
1550
        imageMessenger.removeSemaphore(bucketName + "/" + objectName);
 
1551
        try {
 
1552
            EntityWrapper<ImageCacheInfo> db = new EntityWrapper<ImageCacheInfo>();
 
1553
            ImageCacheInfo searchImageCacheInfo = new ImageCacheInfo(bucketName, objectName);
 
1554
            List<ImageCacheInfo> foundImageCacheInfos = db.query(searchImageCacheInfo);
 
1555
 
 
1556
            if(foundImageCacheInfos.size() > 0) {
 
1557
                ImageCacheInfo foundImageCacheInfo = foundImageCacheInfos.get(0);
 
1558
                if(foundImageCacheInfo.getInCache() && !foundImageCacheInfo.getCaching()) {
 
1559
                    db.delete(foundImageCacheInfo);
 
1560
                    storageManager.deleteObject(bucketName, foundImageCacheInfo.getImageName());
 
1561
                }
 
1562
                db.commit();
 
1563
            } else {
 
1564
                db.rollback();
 
1565
                LOG.warn("Cannot find image in cache" + bucketName + "/" + objectName);
 
1566
            }
 
1567
        } catch(Exception ex) {
 
1568
            LOG.warn(ex, ex);
 
1569
        }
 
1570
    }
 
1571
 
 
1572
    private class ImageCacheFlusher extends Thread {
 
1573
        private String bucketName;
 
1574
        private String objectName;
 
1575
        public ImageCacheFlusher(String bucketName, String objectName) {
 
1576
            this.bucketName = bucketName;
 
1577
            this.objectName = objectName;
 
1578
        }
 
1579
 
 
1580
        public void run() {
 
1581
            flushCachedImage(bucketName, objectName);
 
1582
        }
 
1583
    }
 
1584
 
 
1585
    private class ImageCacher extends Thread {
 
1586
 
 
1587
        private String bucketName;
 
1588
        private String manifestKey;
 
1589
        private String decryptedImageKey;
 
1590
 
 
1591
        public ImageCacher(String bucketName, String manifestKey, String decryptedImageKey) {
 
1592
            this.bucketName = bucketName;
 
1593
            this.manifestKey = manifestKey;
 
1594
            this.decryptedImageKey = decryptedImageKey;
 
1595
        }
 
1596
 
 
1597
        private long tryToCache(String decryptedImageName, String tarredImageName, String imageName) {
 
1598
            Long unencryptedSize = 0L;
 
1599
            boolean failed = false;
 
1600
            try {
 
1601
                unzipImage(decryptedImageName, tarredImageName);
 
1602
                unencryptedSize = untarImage(tarredImageName, imageName);
 
1603
                Long oldCacheSize = 0L;
 
1604
                EntityWrapper<ImageCacheInfo> db = new EntityWrapper<ImageCacheInfo>();
 
1605
                List<ImageCacheInfo> imageCacheInfos = db.query(new ImageCacheInfo());
 
1606
                for(ImageCacheInfo imageCacheInfo: imageCacheInfos) {
 
1607
                    if(imageCacheInfo.getInCache()) {
 
1608
                        oldCacheSize += imageCacheInfo.getSize();
 
1609
                    }
 
1610
                }
 
1611
                db.commit();
 
1612
                if((oldCacheSize + unencryptedSize) > WalrusProperties.IMAGE_CACHE_SIZE) {
 
1613
                    failed = true;
 
1614
                }
 
1615
            } catch(Exception ex) {
 
1616
                LOG.warn(ex, ex);
 
1617
                //try to evict an entry and try again
 
1618
                failed = true;
 
1619
            }
 
1620
            if(failed) {
 
1621
                try {
 
1622
                    storageManager.deleteAbsoluteObject(decryptedImageName);
 
1623
                    storageManager.deleteAbsoluteObject(tarredImageName);
 
1624
                } catch (Exception exception) {
 
1625
                    LOG.warn(exception, exception);
 
1626
                }
 
1627
                return -1L;
 
1628
            }
 
1629
            return unencryptedSize;
 
1630
        }
 
1631
 
 
1632
        public void run() {
 
1633
            //update status
 
1634
            //wake up any waiting consumers
 
1635
            String decryptedImageName = storageManager.getObjectPath(bucketName, decryptedImageKey);
 
1636
            String tarredImageName = decryptedImageName.replaceAll("tgz", "tar");
 
1637
            String imageName = tarredImageName.replaceAll(".tar", "");
 
1638
            String imageKey = decryptedImageKey.replaceAll(".tgz", "");
 
1639
            Long unencryptedSize;
 
1640
            while((unencryptedSize = tryToCache(decryptedImageName, tarredImageName, imageName)) < 0) {
 
1641
                EntityWrapper<ImageCacheInfo> db = new EntityWrapper<ImageCacheInfo>();
 
1642
                List<ImageCacheInfo> imageCacheInfos = db.query(new ImageCacheInfo());
 
1643
                ImageCacheInfo imageCacheInfo = null;
 
1644
                if(imageCacheInfos.size() > 1) {
 
1645
                    Collections.sort(imageCacheInfos);
 
1646
                    imageCacheInfo = imageCacheInfos.get(0);
 
1647
                    break;
 
1648
                }
 
1649
                db.commit();
 
1650
                if(imageCacheInfo != null && imageCacheInfo.getInCache()) {
 
1651
                    flushCachedImage(imageCacheInfo.getBucketName(), imageCacheInfo.getManifestName());
 
1652
                }
 
1653
            }
 
1654
            try {
 
1655
                storageManager.deleteAbsoluteObject(decryptedImageName);
 
1656
                storageManager.deleteAbsoluteObject(tarredImageName);
 
1657
 
 
1658
                EntityWrapper<ImageCacheInfo>db = new EntityWrapper<ImageCacheInfo>();
 
1659
                ImageCacheInfo searchImageCacheInfo = new ImageCacheInfo(bucketName, manifestKey);
 
1660
                List<ImageCacheInfo> foundImageCacheInfos = db.query(searchImageCacheInfo);
 
1661
                if(foundImageCacheInfos.size() > 0) {
 
1662
                    ImageCacheInfo foundImageCacheInfo = foundImageCacheInfos.get(0);
 
1663
                    foundImageCacheInfo.setImageName(imageKey);
 
1664
                    foundImageCacheInfo.setInCache(true);
 
1665
                    foundImageCacheInfo.setCaching(false);
 
1666
                    foundImageCacheInfo.setSize(unencryptedSize);
 
1667
                    db.commit();
 
1668
                    //wake up waiters
 
1669
                    WalrusMonitor monitor = imageMessenger.getMonitor(bucketName + "/" + manifestKey);
 
1670
                    synchronized (monitor) {
 
1671
                        monitor.notifyAll();
 
1672
                    }
 
1673
                    imageMessenger.removeMonitor(bucketName + "/" + manifestKey);
 
1674
 
 
1675
                } else {
 
1676
                    db.rollback();
 
1677
                    LOG.warn("Could not expand image" + decryptedImageName);
 
1678
                }
 
1679
            } catch (Exception ex) {
 
1680
                LOG.warn(ex, ex);
 
1681
            }
 
1682
        }
 
1683
    }
 
1684
 
 
1685
    private void unzipImage(String decryptedImageName, String tarredImageName) throws Exception {
 
1686
        GZIPInputStream in = new GZIPInputStream(new FileInputStream(new File(decryptedImageName)));
 
1687
        File outFile = new File(tarredImageName);
 
1688
        ReadableByteChannel inChannel = Channels.newChannel(in);
 
1689
        WritableByteChannel outChannel = new FileOutputStream(outFile).getChannel();
 
1690
 
 
1691
        ByteBuffer buffer = ByteBuffer.allocate(WalrusQueryDispatcher.DATA_MESSAGE_SIZE);
 
1692
        while (inChannel.read(buffer) != -1) {
 
1693
            buffer.flip();
 
1694
            outChannel.write(buffer);
 
1695
            buffer.clear();
 
1696
        }
 
1697
        outChannel.close();
 
1698
        inChannel.close();
 
1699
    }
 
1700
 
 
1701
    private long untarImage(String tarredImageName, String imageName) throws Exception {
 
1702
        TarInputStream in = new TarInputStream(new FileInputStream(new File(tarredImageName)));
 
1703
        File outFile = new File(imageName);
 
1704
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outFile));
 
1705
 
 
1706
        TarEntry tEntry = in.getNextEntry();
 
1707
        assert(!tEntry.isDirectory());
 
1708
 
 
1709
        in.copyEntryContents(out);
 
1710
        out.close();
 
1711
        in.close();
 
1712
        return outFile.length();
 
1713
    }
 
1714
 
 
1715
    private void decryptImage(String encryptedImageName, String decyptedImageName, Cipher cipher) {
 
1716
        try {
 
1717
            BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(new File(decyptedImageName)));
 
1718
            File inFile = new File(encryptedImageName);
 
1719
            BufferedInputStream in = new BufferedInputStream(new FileInputStream(inFile));
 
1720
 
 
1721
            int bytesRead = 0;
 
1722
            byte[] bytes = new byte[8192];
 
1723
 
 
1724
            while((bytesRead = in.read(bytes)) > 0) {
 
1725
                byte[] outBytes = cipher.update(bytes, 0, bytesRead);
 
1726
                out.write(outBytes);
 
1727
            }
 
1728
            byte[] outBytes = cipher.doFinal();
 
1729
            out.write(outBytes);
 
1730
            in.close();
 
1731
            out.close();
 
1732
        } catch(Exception ex) {
 
1733
            ex.printStackTrace();
 
1734
        }
 
1735
    }
 
1736
 
 
1737
    private void assembleParts(String name, List<String> parts) {
 
1738
        try {
 
1739
            FileChannel out = new FileOutputStream(new File(name)).getChannel();
 
1740
            for (String partName: parts) {
 
1741
                FileChannel in = new FileInputStream(new File(partName)).getChannel();
 
1742
                in.transferTo(0, in.size(), out);
 
1743
                in.close();
 
1744
            }
 
1745
            out.close();
 
1746
        } catch (Exception ex) {
 
1747
            ex.printStackTrace();
 
1748
        }
 
1749
    }
 
1750
 
 
1751
    private byte[] hexToBytes(String data) {
 
1752
        int k = 0;
 
1753
        byte[] results = new byte[data.length() / 2];
 
1754
        for (int i = 0; i < data.length();) {
 
1755
            results[k] = (byte) (Character.digit(data.charAt(i++), 16) << 4);
 
1756
            results[k] += (byte) (Character.digit(data.charAt(i++), 16));
 
1757
            k++;
 
1758
        }
 
1759
 
 
1760
        return results;
 
1761
    }
 
1762
 
 
1763
 
 
1764
    private String bytesToHex(byte[] data) {
 
1765
        StringBuffer buffer = new StringBuffer();
 
1766
        for ( int i = 0; i < data.length; i++ ) {
 
1767
            buffer.append( byteToHex(data[i]) );
 
1768
        }
 
1769
        return(buffer.toString());
 
1770
    }
 
1771
 
 
1772
    private String byteToHex(byte data) {
 
1773
        StringBuffer hexString =  new StringBuffer();
 
1774
        hexString.append(toHex((data>>>4)&0x0F));
 
1775
        hexString.append(toHex(data&0x0F));
 
1776
        return hexString.toString();
 
1777
    }
 
1778
 
 
1779
    private char toHex(int value) {
 
1780
        if ((0 <= value) && (value <= 9 ))
 
1781
            return (char)('0' + value);
 
1782
        else
 
1783
            return (char)('a' + (value-10));
 
1784
    }
 
1785
 
 
1786
    class Reader extends Thread {
 
1787
 
 
1788
        private String bucketName;
 
1789
        private String objectName;
 
1790
        private long objectSize;
 
1791
        private LinkedBlockingQueue<WalrusDataMessage> getQueue;
 
1792
        private long byteRangeStart;
 
1793
        private long byteRangeEnd;
 
1794
        private boolean deleteAfterXfer;
 
1795
        private WalrusSemaphore semaphore;
 
1796
 
 
1797
        public Reader(String bucketName, String objectName, long objectSize, LinkedBlockingQueue<WalrusDataMessage> getQueue) {
 
1798
            this.bucketName = bucketName;
 
1799
            this.objectName = objectName;
 
1800
            this.objectSize = objectSize;
 
1801
            this.getQueue = getQueue;
 
1802
        }
 
1803
 
 
1804
 
 
1805
        public Reader(String bucketName, String objectName, long objectSize, LinkedBlockingQueue<WalrusDataMessage> getQueue, long byteRangeStart, long byteRangeEnd) {
 
1806
            this.bucketName = bucketName;
 
1807
            this.objectName = objectName;
 
1808
            this.objectSize = objectSize;
 
1809
            this.getQueue = getQueue;
 
1810
            this.byteRangeStart = byteRangeStart;
 
1811
            this.byteRangeEnd = byteRangeEnd;
 
1812
        }
 
1813
 
 
1814
        public Reader(String bucketName, String objectName, long objectSize, LinkedBlockingQueue<WalrusDataMessage> getQueue, boolean deleteAfterXfer, WalrusSemaphore semaphore) {
 
1815
            this(bucketName, objectName, objectSize, getQueue);
 
1816
            this.deleteAfterXfer = deleteAfterXfer;
 
1817
            this.semaphore = semaphore;
 
1818
        }
 
1819
 
 
1820
        public void run() {
 
1821
            byte[] bytes = new byte[WalrusQueryDispatcher.DATA_MESSAGE_SIZE];
 
1822
 
 
1823
            long bytesRemaining = objectSize;
 
1824
            long offset = byteRangeStart;
 
1825
 
 
1826
            if(byteRangeEnd != 0) {
 
1827
                assert(byteRangeEnd <= objectSize);
 
1828
                assert(byteRangeEnd >= byteRangeStart);
 
1829
                bytesRemaining = byteRangeEnd - byteRangeStart;
 
1830
            }
 
1831
 
 
1832
            try {
 
1833
                getQueue.put(WalrusDataMessage.StartOfData(bytesRemaining));
 
1834
 
 
1835
                while (bytesRemaining > 0) {
 
1836
                    int bytesRead = storageManager.readObject(bucketName, objectName, bytes, offset);
 
1837
                    bytesRemaining -= bytesRead;
 
1838
                    getQueue.put(WalrusDataMessage.DataMessage(bytes, bytesRead));
 
1839
                    offset += bytesRead;
 
1840
                }
 
1841
                getQueue.put(WalrusDataMessage.EOF());
 
1842
            } catch (Exception ex) {
 
1843
                LOG.error( ex,ex );
 
1844
                //TODO: set error code
 
1845
            }
 
1846
            if(semaphore != null) {
 
1847
                semaphore.release();
 
1848
                synchronized (semaphore) {
 
1849
                    semaphore.notifyAll();
 
1850
                }
 
1851
            }
 
1852
            if(deleteAfterXfer) {
 
1853
                try {
 
1854
                    storageManager.deleteObject(bucketName, objectName);
 
1855
                } catch(Exception ex) {
 
1856
                    LOG.error( ex,ex );
 
1857
                }
 
1858
            }
 
1859
        }
 
1860
    }
 
1861
 
 
1862
    public StoreSnapshotResponseType StoreSnapshot(StoreSnapshotType request) throws EucalyptusCloudException {
 
1863
        StoreSnapshotResponseType reply = (StoreSnapshotResponseType) request.getReply();
 
1864
        String volumeId = request.getVolumeId();
 
1865
        String snapshotId = request.getKey();
 
1866
 
 
1867
        EntityWrapper<WalrusVolumeInfo> db = new EntityWrapper<WalrusVolumeInfo>();
 
1868
        WalrusVolumeInfo volumeInfo = new WalrusVolumeInfo(volumeId);
 
1869
        List<WalrusVolumeInfo> foundVolumeInfos = db.query(volumeInfo);
 
1870
        WalrusVolumeInfo foundVolumeInfo = null;
 
1871
 
 
1872
        if(foundVolumeInfos.size() == 0) {
 
1873
            foundVolumeInfo = volumeInfo;
 
1874
            db.add(foundVolumeInfo);
 
1875
        } else {
 
1876
            foundVolumeInfo = foundVolumeInfos.get(0);
 
1877
        }
 
1878
 
 
1879
        WalrusSnapshotInfo snapshotInfo = new WalrusSnapshotInfo(volumeId, snapshotId);
 
1880
//read and store it
 
1881
        List<WalrusSnapshotInfo> snapshotSet = foundVolumeInfo.getSnapshotSet();
 
1882
        for(WalrusSnapshotInfo snapInfo: snapshotSet) {
 
1883
            if(snapInfo.getSnapshotId().equals(snapshotId)) {
 
1884
                db.rollback();
 
1885
                throw new EucalyptusCloudException();
 
1886
            }
 
1887
        }
 
1888
        snapshotSet.add(snapshotInfo);
 
1889
        db.commit();
 
1890
//convert to a PutObject request
 
1891
        PutObjectType putObjectRequest = new PutObjectType();
 
1892
        putObjectRequest.setBucket(request.getBucket());
 
1893
        putObjectRequest.setKey(snapshotId);
 
1894
        putObjectRequest.setRandomKey(request.getRandomKey());
 
1895
        PutObjectResponseType putObjectResponseType = PutObject(putObjectRequest);
 
1896
        reply.setEtag(putObjectResponseType.getEtag());
 
1897
        reply.setLastModified(putObjectResponseType.getLastModified());
 
1898
        reply.setStatusMessage(putObjectResponseType.getStatusMessage());
 
1899
        return reply;
 
1900
    }
 
1901
 
 
1902
    public GetSnapshotInfoResponseType GetSnapshotInfo(GetSnapshotInfoType request) throws EucalyptusCloudException {
 
1903
        GetSnapshotInfoResponseType reply = (GetSnapshotInfoResponseType)request.getReply();
 
1904
        String snapshotId = request.getKey();
 
1905
 
 
1906
        EntityWrapper<WalrusSnapshotInfo> db = new EntityWrapper<WalrusSnapshotInfo>();
 
1907
        WalrusSnapshotInfo snapshotInfo = new WalrusSnapshotInfo(snapshotId);
 
1908
        List<WalrusSnapshotInfo> foundSnapshotInfos = db.query(snapshotInfo);
 
1909
        db.commit();
 
1910
        if(foundSnapshotInfos.size() > 0) {
 
1911
            WalrusSnapshotInfo foundSnapshotInfo = foundSnapshotInfos.get(0);
 
1912
            String volumeId = foundSnapshotInfo.getVolumeId();
 
1913
            WalrusVolumeInfo volumeInfo = new WalrusVolumeInfo(volumeId);
 
1914
            EntityWrapper<WalrusVolumeInfo> db2 = new EntityWrapper<WalrusVolumeInfo>();
 
1915
            List<WalrusVolumeInfo> foundVolumeInfos = db2.query(volumeInfo);
 
1916
            if(foundVolumeInfos.size() > 0) {
 
1917
                WalrusVolumeInfo foundVolumeInfo = foundVolumeInfos.get(0);
 
1918
                List<String> snapshotNames = reply.getSnapshotSet();
 
1919
                snapshotNames.add(volumeId);
 
1920
                for(WalrusSnapshotInfo snapInfo: foundVolumeInfo.getSnapshotSet()) {
 
1921
                    snapshotNames.add(snapInfo.getSnapshotId());
 
1922
                }
 
1923
                db2.commit();
 
1924
            } else {
 
1925
                db2.rollback();
 
1926
                throw new EucalyptusCloudException();
 
1927
            }
 
1928
        } else {
 
1929
            throw new EucalyptusCloudException();
 
1930
        }
 
1931
        return reply;
 
1932
    }
 
1933
 
 
1934
    public GetSnapshotResponseType GetSnapshot(GetSnapshotType request) throws EucalyptusCloudException {
 
1935
        GetSnapshotResponseType reply = (GetSnapshotResponseType) request.getReply();
 
1936
        String snapshotId = request.getKey();
 
1937
 
 
1938
        return reply;
 
1939
    }
 
1940
 
 
1941
    public RemoveSnapshotResponseType RemoveSnapshot(RemoveSnapshotType request) throws EucalyptusCloudException {
 
1942
        RemoveSnapshotResponseType reply = (RemoveSnapshotResponseType) request.getReply();
 
1943
 
 
1944
        return reply;
 
1945
    }
 
1946
}