| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  |  * Minio Cloud Storage, (C) 2015-2016 Minio, Inc. | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package fs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 	"hash/fnv" | 
					
						
							| 
									
										
										
										
											2016-01-27 13:55:50 +08:00
										 |  |  | 	"net/url" | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | 	"os" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/minio/minio-xl/pkg/probe" | 
					
						
							| 
									
										
										
										
											2016-02-05 04:52:25 +08:00
										 |  |  | 	"github.com/minio/minio/pkg/ioutils" | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-27 03:48:52 +08:00
										 |  |  | // listObjectsParams - list objects input parameters.
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | type listObjectsParams struct { | 
					
						
							| 
									
										
										
										
											2016-01-27 03:48:52 +08:00
										 |  |  | 	// Bucket name to list the objects for.
 | 
					
						
							|  |  |  | 	Bucket string | 
					
						
							|  |  |  | 	// list all objects with this parameter as common prefix.
 | 
					
						
							|  |  |  | 	Prefix string | 
					
						
							|  |  |  | 	// list all objects starting with object after marker in
 | 
					
						
							|  |  |  | 	// lexicographical order.
 | 
					
						
							|  |  |  | 	Marker string | 
					
						
							|  |  |  | 	// list all objects until the first occurrence of the delimtier
 | 
					
						
							|  |  |  | 	// after the prefix.
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 	Delimiter string | 
					
						
							| 
									
										
										
										
											2016-01-27 03:48:52 +08:00
										 |  |  | 	// maximum number of objects returned per listObjects()
 | 
					
						
							|  |  |  | 	// operation.
 | 
					
						
							|  |  |  | 	MaxKeys int | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-27 03:48:52 +08:00
										 |  |  | // listServiceReq
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | type listServiceReq struct { | 
					
						
							|  |  |  | 	reqParams listObjectsParams | 
					
						
							|  |  |  | 	respCh    chan ListObjectsResult | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type listWorkerReq struct { | 
					
						
							|  |  |  | 	respCh chan ListObjectsResult | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-27 03:48:52 +08:00
										 |  |  | // listObjects - list objects lists objects upto maxKeys for a given prefix.
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | func (fs Filesystem) listObjects(bucket, prefix, marker, delimiter string, maxKeys int) (chan<- listWorkerReq, *probe.Error) { | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 	quitWalker := make(chan bool) | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 	reqCh := make(chan listWorkerReq) | 
					
						
							|  |  |  | 	walkerCh := make(chan ObjectMetadata) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 		defer close(walkerCh) | 
					
						
							|  |  |  | 		var walkPath string | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 		bucketPath := filepath.Join(fs.path, bucket) | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 		// Bucket path prefix should always end with a separator.
 | 
					
						
							|  |  |  | 		bucketPathPrefix := bucketPath + string(os.PathSeparator) | 
					
						
							|  |  |  | 		prefixPath := bucketPathPrefix + prefix | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 		st, err := os.Stat(prefixPath) | 
					
						
							|  |  |  | 		if err != nil && os.IsNotExist(err) { | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 			walkPath = bucketPath | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			if st.IsDir() && !strings.HasSuffix(prefix, delimiter) { | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 				walkPath = bucketPath | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 				walkPath = prefixPath | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-02-05 18:15:48 +08:00
										 |  |  | 		ioutils.FTW(walkPath, func(path string, info os.FileInfo, e error) error { | 
					
						
							|  |  |  | 			if e != nil { | 
					
						
							|  |  |  | 				return e | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-02-06 12:05:56 +08:00
										 |  |  | 			// Skip special temporary files, kept for multipart transaction.
 | 
					
						
							|  |  |  | 			if strings.Contains(path, "$multiparts") || strings.Contains(path, "$tmpobject") { | 
					
						
							| 
									
										
										
										
											2016-02-05 18:15:48 +08:00
										 |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 			// We don't need to list the walk path.
 | 
					
						
							|  |  |  | 			if path == walkPath { | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-01-28 18:50:40 +08:00
										 |  |  | 			if info.IsDir() && delimiter == "" { | 
					
						
							|  |  |  | 				return nil | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 			// For all incoming directories add a ending separator.
 | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 			if info.IsDir() { | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 				path = path + string(os.PathSeparator) | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 			// Extract object name.
 | 
					
						
							|  |  |  | 			objectName := strings.TrimPrefix(path, bucketPathPrefix) | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 			if strings.HasPrefix(objectName, prefix) { | 
					
						
							|  |  |  | 				object := ObjectMetadata{ | 
					
						
							|  |  |  | 					Object:  objectName, | 
					
						
							|  |  |  | 					Created: info.ModTime(), | 
					
						
							|  |  |  | 					Mode:    info.Mode(), | 
					
						
							|  |  |  | 					Size:    info.Size(), | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				select { | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 				// Send object on walker channel.
 | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 				case walkerCh <- object: | 
					
						
							|  |  |  | 				case <-quitWalker: | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 					// Returning error ends the file tree Walk().
 | 
					
						
							|  |  |  | 					return errors.New("Quit list worker.") | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 				// If delimiter is set, we stop if current path is a
 | 
					
						
							|  |  |  | 				// directory.
 | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 				if delimiter != "" && info.IsDir() { | 
					
						
							| 
									
										
										
										
											2016-02-05 04:52:25 +08:00
										 |  |  | 					return ioutils.ErrSkipDir | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 			return nil | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 	go func() { | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							| 
									
										
										
										
											2016-01-28 18:50:40 +08:00
										 |  |  | 			// Timeout after 1 seconds if request did not arrive for
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 			// the given list parameters.
 | 
					
						
							| 
									
										
										
										
											2016-01-28 18:50:40 +08:00
										 |  |  | 			case <-time.After(1 * time.Second): | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 				quitWalker <- true // Quit file path walk if running.
 | 
					
						
							|  |  |  | 				// Send back the hash for this request.
 | 
					
						
							|  |  |  | 				fs.timeoutReqCh <- fnvSum(bucket, prefix, marker, delimiter) | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 				return | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 			case req, ok := <-reqCh: | 
					
						
							|  |  |  | 				if !ok { | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 					// If the request channel is closed, no more
 | 
					
						
							|  |  |  | 					// requests return here.
 | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 				resp := ListObjectsResult{} | 
					
						
							|  |  |  | 				var count int | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 				for object := range walkerCh { | 
					
						
							| 
									
										
										
										
											2016-01-28 18:50:40 +08:00
										 |  |  | 					// Verify if the object is lexically smaller than
 | 
					
						
							|  |  |  | 					// the marker, we will skip those objects.
 | 
					
						
							|  |  |  | 					if marker >= object.Object { | 
					
						
							|  |  |  | 						continue | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2016-01-28 18:50:40 +08:00
										 |  |  | 					if delimiter != "" { | 
					
						
							|  |  |  | 						// Prefixes are only valid wth delimiters, and
 | 
					
						
							|  |  |  | 						// for filesystem backend they are only valid
 | 
					
						
							|  |  |  | 						// if they are directories.
 | 
					
						
							|  |  |  | 						if object.Mode.IsDir() { | 
					
						
							|  |  |  | 							resp.Prefixes = append(resp.Prefixes, object.Object) | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							// Rest of them are treated as files.
 | 
					
						
							|  |  |  | 							resp.Objects = append(resp.Objects, object) | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | 						} | 
					
						
							|  |  |  | 					} else { | 
					
						
							| 
									
										
										
										
											2016-01-28 18:50:40 +08:00
										 |  |  | 						// In-case of no delimiters, there are no
 | 
					
						
							|  |  |  | 						// prefixes all are considered to be objects.
 | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 						resp.Objects = append(resp.Objects, object) | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2016-01-28 18:50:40 +08:00
										 |  |  | 					count++ // Bump the counter
 | 
					
						
							|  |  |  | 					// Verify if we have reached the maxKeys requested.
 | 
					
						
							|  |  |  | 					if count == maxKeys { | 
					
						
							|  |  |  | 						if delimiter != "" { | 
					
						
							|  |  |  | 							// Set the next marker for the next request.
 | 
					
						
							|  |  |  | 							// This element is set only if you have delimiter set.
 | 
					
						
							|  |  |  | 							// If response does not include the NextMaker and it is
 | 
					
						
							|  |  |  | 							// truncated, you can use the value of the last Key in the
 | 
					
						
							|  |  |  | 							// response as the marker in the subsequent request to get the
 | 
					
						
							|  |  |  | 							// next set of object keys.
 | 
					
						
							|  |  |  | 							if len(resp.Objects) > 0 { | 
					
						
							|  |  |  | 								// NextMarker is only set when there
 | 
					
						
							|  |  |  | 								// are more than maxKeys worth of
 | 
					
						
							|  |  |  | 								// objects for a given prefix path.
 | 
					
						
							|  |  |  | 								resp.NextMarker = resp.Objects[len(resp.Objects)-1:][0].Object | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						// Set truncated boolean to indicate the
 | 
					
						
							|  |  |  | 						// client to send the next batch of requests.
 | 
					
						
							|  |  |  | 						resp.IsTruncated = true | 
					
						
							|  |  |  | 						break | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-01-28 18:50:40 +08:00
										 |  |  | 				// Set the marker right here for the new set of the
 | 
					
						
							|  |  |  | 				// values coming in the from the client.
 | 
					
						
							|  |  |  | 				marker = resp.NextMarker | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 				req.respCh <- resp | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	return reqCh, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | // fnvSum calculates a hash for concatenation of all input strings.
 | 
					
						
							|  |  |  | func fnvSum(elements ...string) uint32 { | 
					
						
							|  |  |  | 	fnvHash := fnv.New32a() | 
					
						
							|  |  |  | 	for _, element := range elements { | 
					
						
							|  |  |  | 		fnvHash.Write([]byte(element)) | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 	return fnvHash.Sum32() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // listObjectsService - list objects service manages various incoming
 | 
					
						
							|  |  |  | // list object requests by delegating them to an existing listObjects
 | 
					
						
							|  |  |  | // routine or initializes a new listObjects routine.
 | 
					
						
							|  |  |  | func (fs *Filesystem) listObjectsService() *probe.Error { | 
					
						
							|  |  |  | 	// Initialize list service request channel.
 | 
					
						
							|  |  |  | 	listServiceReqCh := make(chan listServiceReq) | 
					
						
							|  |  |  | 	fs.listServiceReqCh = listServiceReqCh | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize timeout request channel to receive request hashes of
 | 
					
						
							|  |  |  | 	// timed-out requests.
 | 
					
						
							|  |  |  | 	timeoutReqCh := make(chan uint32) | 
					
						
							|  |  |  | 	fs.timeoutReqCh = timeoutReqCh | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize request hash to list worker map.
 | 
					
						
							|  |  |  | 	reqToListWorkerReqCh := make(map[uint32]chan<- listWorkerReq) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Start service in a go routine.
 | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 	go func() { | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 			case reqHash := <-timeoutReqCh: | 
					
						
							|  |  |  | 				// For requests which have timed-out, close the worker
 | 
					
						
							|  |  |  | 				// channels proactively, this may happen for idle
 | 
					
						
							|  |  |  | 				// workers once in 10seconds.
 | 
					
						
							|  |  |  | 				listWorkerReqCh, ok := reqToListWorkerReqCh[reqHash] | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 				if ok { | 
					
						
							|  |  |  | 					close(listWorkerReqCh) | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 				delete(reqToListWorkerReqCh, reqHash) | 
					
						
							|  |  |  | 			case srvReq := <-listServiceReqCh: | 
					
						
							|  |  |  | 				// Save the params for readability.
 | 
					
						
							|  |  |  | 				bucket := srvReq.reqParams.Bucket | 
					
						
							|  |  |  | 				prefix := srvReq.reqParams.Prefix | 
					
						
							|  |  |  | 				marker := srvReq.reqParams.Marker | 
					
						
							|  |  |  | 				delimiter := srvReq.reqParams.Delimiter | 
					
						
							|  |  |  | 				maxKeys := srvReq.reqParams.MaxKeys | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// Generate hash.
 | 
					
						
							|  |  |  | 				reqHash := fnvSum(bucket, prefix, marker, delimiter) | 
					
						
							|  |  |  | 				listWorkerReqCh, ok := reqToListWorkerReqCh[reqHash] | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 				if !ok { | 
					
						
							|  |  |  | 					var err *probe.Error | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 					listWorkerReqCh, err = fs.listObjects(bucket, prefix, marker, delimiter, maxKeys) | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 					if err != nil { | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 						srvReq.respCh <- ListObjectsResult{} | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 						return | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 					reqToListWorkerReqCh[reqHash] = listWorkerReqCh | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 				respCh := make(chan ListObjectsResult) | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 				listWorkerReqCh <- listWorkerReq{respCh} | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 				resp, ok := <-respCh | 
					
						
							|  |  |  | 				if !ok { | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 					srvReq.respCh <- ListObjectsResult{} | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 				delete(reqToListWorkerReqCh, reqHash) | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 				if !resp.IsTruncated { | 
					
						
							|  |  |  | 					close(listWorkerReqCh) | 
					
						
							|  |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 					nextMarker := resp.NextMarker | 
					
						
							|  |  |  | 					reqHash = fnvSum(bucket, prefix, nextMarker, delimiter) | 
					
						
							|  |  |  | 					reqToListWorkerReqCh[reqHash] = listWorkerReqCh | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 				srvReq.respCh <- resp | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 	}() | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | // ListObjects - lists all objects for a given prefix, returns upto
 | 
					
						
							|  |  |  | // maxKeys number of objects per call.
 | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | func (fs Filesystem) ListObjects(bucket, prefix, marker, delimiter string, maxKeys int) (ListObjectsResult, *probe.Error) { | 
					
						
							| 
									
										
										
										
											2016-02-05 12:40:58 +08:00
										 |  |  | 	// Input validation.
 | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 	if !IsValidBucketName(bucket) { | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 		return ListObjectsResult{}, probe.NewError(BucketNameInvalid{Bucket: bucket}) | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bucket = fs.denormalizeBucket(bucket) | 
					
						
							|  |  |  | 	rootPrefix := filepath.Join(fs.path, bucket) | 
					
						
							| 
									
										
										
										
											2016-02-05 12:40:58 +08:00
										 |  |  | 	// Check bucket exists.
 | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 	if _, e := os.Stat(rootPrefix); e != nil { | 
					
						
							|  |  |  | 		if os.IsNotExist(e) { | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 			return ListObjectsResult{}, probe.NewError(BucketNotFound{Bucket: bucket}) | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 		return ListObjectsResult{}, probe.NewError(e) | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-27 13:55:50 +08:00
										 |  |  | 	// Unescape the marker values.
 | 
					
						
							|  |  |  | 	markerUnescaped, e := url.QueryUnescape(marker) | 
					
						
							|  |  |  | 	if e != nil { | 
					
						
							|  |  |  | 		return ListObjectsResult{}, probe.NewError(e) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 	reqParams := listObjectsParams{} | 
					
						
							|  |  |  | 	reqParams.Bucket = bucket | 
					
						
							|  |  |  | 	reqParams.Prefix = filepath.FromSlash(prefix) | 
					
						
							| 
									
										
										
										
											2016-01-27 13:55:50 +08:00
										 |  |  | 	reqParams.Marker = filepath.FromSlash(markerUnescaped) | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 	reqParams.Delimiter = filepath.FromSlash(delimiter) | 
					
						
							|  |  |  | 	reqParams.MaxKeys = maxKeys | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 	respCh := make(chan ListObjectsResult) | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 	fs.listServiceReqCh <- listServiceReq{reqParams, respCh} | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 	resp := <-respCh | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 	for i := range resp.Prefixes { | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 		resp.Prefixes[i] = filepath.ToSlash(resp.Prefixes[i]) | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-01-26 18:19:55 +08:00
										 |  |  | 	for i := range resp.Objects { | 
					
						
							| 
									
										
										
										
											2016-01-20 09:49:48 +08:00
										 |  |  | 		resp.Objects[i].Object = filepath.ToSlash(resp.Objects[i].Object) | 
					
						
							| 
									
										
										
										
											2015-11-10 19:10:11 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return resp, nil | 
					
						
							| 
									
										
										
										
											2015-10-23 06:37:45 +08:00
										 |  |  | } |