diff --git a/Source/Api/Api.csproj b/Source/Api/Api.csproj
index 809e808..ebd6551 100644
--- a/Source/Api/Api.csproj
+++ b/Source/Api/Api.csproj
@@ -30,7 +30,7 @@
-
+
diff --git a/Source/Api/Controllers/ClonesController.cs b/Source/Api/Controllers/ClonesController.cs
new file mode 100644
index 0000000..df32918
--- /dev/null
+++ b/Source/Api/Controllers/ClonesController.cs
@@ -0,0 +1,74 @@
+using Api.Core;
+using Api.Models;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using ROOT.Zfs.Public;
+using ROOT.Zfs.Public.Arguments.Dataset;
+using ROOT.Zfs.Public.Data;
+using System.Collections.Generic;
+using ROOT.Zfs.Public.Data.Datasets;
+using System.Linq;
+using ROOT.Zfs.Public.Arguments.Snapshots;
+
+namespace Api.Controllers
+{
+ ///
+ /// Contains methods for manipulating clones of snapshots, i.e.
+ /// datasets that is clones of a snapshot
+ ///
+ [Authorize]
+ [ApiController]
+ public class ClonesController : ApiControllerBase
+ {
+ private readonly IZfs _zfs;
+
+ ///
+ public ClonesController(IZfsAccessor zfsAccessor)
+ {
+ _zfs = zfsAccessor.Zfs;
+ }
+
+ ///
+ /// Lists datasets that are clones of snapshots
+ ///
+ /// A list of datasets that are clones
+ [HttpGet("/api/zfs/clones")]
+ public Response> ListClones()
+ {
+ DatasetListArgs args = new DatasetListArgs
+ {
+ DatasetTypes = DatasetTypes.Filesystem | DatasetTypes.Volume
+ };
+ var datasets = _zfs.Datasets.List(args);
+ var clones = datasets.Where(d => d.IsClone);
+ return new Response> { Data = clones };
+ }
+
+ [HttpPost("/api/zfs/clones/{dataset}/{snapshot}")]
+ public Response CreateClone(string dataset, string snapshot, [FromBody] CloneRequest request)
+ {
+ var args = new SnapshotCloneArgs { Dataset = dataset, Snapshot = snapshot, TargetDataset = request.Dataset };
+ if (request.Properties != null && request.Properties.Length > 0)
+ {
+ var properties = request.Properties.Select(p => new PropertyValue { Property = p.Name, Value = p.Value }).ToList();
+ args.Properties = properties;
+ }
+ _zfs.Snapshots.Clone(args);
+ return new Response();
+ }
+
+ ///
+ /// Promotes a clone into an independent dataset.
+ /// When you promote a clone, it reverses the relationship between the original dataset and the clone.
+ /// So the clone becomes the "master" and the original becomes a dependent dataset
+ ///
+ /// The name of the dataset that should be promoted from a clone to its own dataset
+ [HttpPut("/api/zfs/clones/{name}")]
+ public Response PromoteClone(string name)
+ {
+ var args = new PromoteArgs { Name = name };
+ _zfs.Datasets.Promote(args);
+ return new Response();
+ }
+ }
+}
diff --git a/Source/Api/Models/CloneRequest.cs b/Source/Api/Models/CloneRequest.cs
new file mode 100644
index 0000000..6fc09b9
--- /dev/null
+++ b/Source/Api/Models/CloneRequest.cs
@@ -0,0 +1,19 @@
+namespace Api.Models
+{
+ ///
+ /// Encapsulates the data required to clone a dataset to be independent
+ ///
+ public class CloneRequest
+ {
+ ///
+ /// Name of the dataset that should be promoted
+ ///
+ public string Dataset { get; set; }
+
+ ///
+ /// Optional properties to set on the promoted dataset, if they should be different from original
+ ///
+ public PropertyData[] Properties { get; set; }
+
+ }
+}